1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.shiro.authc.credential;
20
21 import org.apache.shiro.authc.AuthenticationInfo;
22 import org.apache.shiro.authc.AuthenticationToken;
23 import org.apache.shiro.authc.SaltedAuthenticationInfo;
24 import org.apache.shiro.crypto.hash.Hash;
25 import org.apache.shiro.crypto.hash.SimpleHash;
26 import org.apache.shiro.lang.codec.Base64;
27 import org.apache.shiro.lang.codec.Hex;
28 import org.apache.shiro.lang.util.SimpleByteSource;
29 import org.apache.shiro.lang.util.StringUtils;
30
31 import static java.util.Objects.requireNonNull;
32
33 /**
34 * A {@code HashedCredentialMatcher} provides support for hashing of supplied {@code AuthenticationToken} credentials
35 * before being compared to those in the {@code AuthenticationInfo} from the data store.
36 * <p/>
37 * Credential hashing is one of the most common security techniques when safeguarding a user's private credentials
38 * (passwords, keys, etc.). Most developers never want to store their users' credentials in plain form, viewable by
39 * anyone, so they often hash the users' credentials before they are saved in the data store.
40 * <p/>
41 * This class (and its subclasses) function as follows:
42 * <ol>
43 * <li>Hash the {@code AuthenticationToken} credentials supplied by the user during their login.</li>
44 * <li>Compare this hashed value directly with the {@code AuthenticationInfo} credentials stored in the system
45 * (the stored account credentials are expected to already be in hashed form).</li>
46 * <li>If these two values are {@link #equals(Object, Object) equal}, the submitted credentials match, otherwise
47 * they do not.</li>
48 * </ol>
49 * <h2>Salting and Multiple Hash Iterations</h2>
50 * Because simple hashing is usually not good enough for secure applications, this class also supports 'salting'
51 * and multiple hash iterations. Please read this excellent
52 * <a href="http://www.owasp.org/index.php/Hashing_Java" _target="blank">Hashing Java article</a> to learn about
53 * salting and multiple iterations and why you might want to use them. (Note of sections 5
54 * "Why add salt?" and 6 "Hardening against the attacker's attack").</p>
55 * <h4>Real World Case Study</h4>
56 * In April 2010, some public Atlassian Jira and Confluence
57 * installations (Apache Software Foundation, Codehaus, etc.) were the target of account attacks and user accounts
58 * were compromised. The reason? Jira and Confluence at the time did not salt user passwords and attackers were
59 * able to use dictionary attacks to compromise user accounts (Atlassian has since
60 * <a href="http://blogs.atlassian.com/news/2010/04/oh_man_what_a_day_an_update_on_our_security_breach.html">
61 * fixed the problem</a> of course).
62 * <p/>
63 * The lesson?
64 * <p/>
65 * <b>ALWAYS, ALWAYS, ALWAYS SALT USER PASSWORDS!</b>
66 * <p/>
67 * <h3>Salting</h3>
68 * Prior to Shiro 1.1, salts could be obtained based on the end-user submitted
69 * {@link AuthenticationToken AuthenticationToken} via the now-deprecated
70 * {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getSalt(AuthenticationToken)} method. This however
71 * could constitute a security hole since ideally salts should never be obtained based on what a user can submit.
72 * User-submitted salt mechanisms are <em>much</em> more susceptible to dictionary attacks and <b>SHOULD NOT</b> be
73 * used in secure systems. Instead, salts should ideally be a secure randomly-generated number that is generated when
74 * the user account is created. The secure number should never be disseminated to the user and always kept private
75 * by the application.
76 * <h4>Shiro 1.1</h4>
77 * As of Shiro 1.1, it is expected that any salt used to hash the submitted credentials will be obtained from the
78 * stored account information (represented as an {@link AuthenticationInfo AuthenticationInfo} instance). This is much
79 * more secure because the salt value remains private to the application (Shiro will never store this value).
80 * <p/>
81 * To enable this, {@code Realm}s should return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances
82 * during authentication. {@code HashedCredentialsMatcher} implementations will then use the provided
83 * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt credentialsSalt} for hashing. To avoid
84 * security risks,
85 * <b>it is highly recommended that any existing {@code Realm} implementations that support hashed credentials are
86 * updated to return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances as soon as possible</b>.
87 * <h4>Shiro 1.0 Backwards Compatibility</h4>
88 * Because of the identified security risk, {@code Realm} implementations that support credentials hashing should
89 * be updated to return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances as
90 * soon as possible.
91 * <p/>
92 * If this is not possible for some reason, this class will retain 1.0 backwards-compatible behavior of obtaining
93 * the salt via the now-deprecated {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)} method. This
94 * method will only be invoked if a {@code Realm} <em>does not</em> return
95 * {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances and {@link #isHashSalted() hashSalted} is
96 * {@code true}.
97 * But please note that the {@link #isHashSalted() hashSalted} property and the
98 * {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)} methods will be removed before the Shiro 2.0
99 * release.
100 * <h3>Multiple Hash Iterations</h3>
101 * If you hash your users' credentials multiple times before persisting to the data store, you will also need to
102 * set this class's {@link #setHashIterations(int) hashIterations} property. See the
103 * <a href="http://www.owasp.org/index.php/Hashing_Java" _target="blank">Hashing Java article</a>'s
104 * <a href="http://www.owasp.org/index.php/Hashing_Java#Hardening_against_the_attacker.27s_attack">
105 * "Hardening against the attacker's attack"</a> section to learn more about why you might want to use
106 * multiple hash iterations.
107 * <h2>MD5 & SHA-1 Notice</h2>
108 * <a href="http://en.wikipedia.org/wiki/MD5">MD5</a> and
109 * <a href="http://en.wikipedia.org/wiki/SHA_hash_functions">SHA-1</a> algorithms are now known to be vulnerable to
110 * compromise and/or collisions (read the linked pages for more). While most applications are ok with either of these
111 * two, if your application mandates high security, use the SHA-256 (or higher) hashing algorithms and their
112 * supporting {@code CredentialsMatcher} implementations.
113 *
114 * @see org.apache.shiro.crypto.hash.Sha256Hash
115 * @see org.apache.shiro.crypto.hash.Sha384Hash
116 * @see org.apache.shiro.crypto.hash.Sha256Hash
117 * @since 0.9
118 */
119 public class HashedCredentialsMatcher extends SimpleCredentialsMatcher {
120
121 /**
122 * @since 1.1
123 */
124 private String hashAlgorithm;
125 private int hashIterations;
126 private boolean hashSalted;
127 private boolean storedCredentialsHexEncoded;
128
129 /**
130 * JavaBeans-compatible no-arg constructor intended for use in IoC/Dependency Injection environments. If you
131 * use this constructor, you <em>MUST</em> also additionally set the
132 * {@link #setHashAlgorithmName(String) hashAlgorithmName} property.
133 */
134 public HashedCredentialsMatcher() {
135 this.hashAlgorithm = null;
136 this.hashSalted = false;
137 this.hashIterations = 1;
138 //false means Base64-encoded
139 this.storedCredentialsHexEncoded = true;
140 }
141
142 /**
143 * Creates an instance using the specified {@link #getHashAlgorithmName() hashAlgorithmName} to hash submitted
144 * credentials.
145 *
146 * @param hashAlgorithmName the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName}
147 * to use when performing hashes for credentials matching.
148 * @since 1.1
149 */
150 public HashedCredentialsMatcher(String hashAlgorithmName) {
151 this();
152 if (!StringUtils.hasText(hashAlgorithmName)) {
153 throw new IllegalArgumentException("hashAlgorithmName cannot be null or empty.");
154 }
155 this.hashAlgorithm = hashAlgorithmName;
156 }
157
158 /**
159 * Returns the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} to use
160 * when performing hashes for credentials matching.
161 *
162 * @return the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} to use
163 * when performing hashes for credentials matching.
164 * @since 1.1
165 */
166 public String getHashAlgorithmName() {
167 return hashAlgorithm;
168 }
169
170 /**
171 * Sets the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} to use
172 * when performing hashes for credentials matching.
173 *
174 * @param hashAlgorithmName the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName}
175 * to use when performing hashes for credentials matching.
176 * @since 1.1
177 */
178 public void setHashAlgorithmName(String hashAlgorithmName) {
179 this.hashAlgorithm = hashAlgorithmName;
180 }
181
182 /**
183 * Returns {@code true} if the system's stored credential hash is Hex encoded, {@code false} if it
184 * is Base64 encoded.
185 * <p/>
186 * Default value is {@code true} for convenience - all of Shiro's {@link Hash Hash#toString()}
187 * implementations return Hex encoded values by default, making this class's use with those implementations
188 * easier.
189 *
190 * @return {@code true} if the system's stored credential hash is Hex encoded, {@code false} if it
191 * is Base64 encoded. Default is {@code true}
192 */
193 public boolean isStoredCredentialsHexEncoded() {
194 return storedCredentialsHexEncoded;
195 }
196
197 /**
198 * Sets the indicator if this system's stored credential hash is Hex encoded or not.
199 * <p/>
200 * A value of {@code true} will cause this class to decode the system credential from Hex, a
201 * value of {@code false} will cause this class to decode the system credential from Base64.
202 * <p/>
203 * Unless overridden via this method, the default value is {@code true} for convenience - all of Shiro's
204 * {@link Hash Hash#toString()} implementations return Hex encoded values by default, making this class's use with
205 * those implementations easier.
206 *
207 * @param storedCredentialsHexEncoded the indicator if this system's stored credential hash is Hex
208 * encoded or not ('not' automatically implying it is Base64 encoded).
209 */
210 public void setStoredCredentialsHexEncoded(boolean storedCredentialsHexEncoded) {
211 this.storedCredentialsHexEncoded = storedCredentialsHexEncoded;
212 }
213
214 /**
215 * Returns {@code true} if a submitted {@code AuthenticationToken}'s credentials should be salted when hashing,
216 * {@code false} if it should not be salted.
217 * <p/>
218 * If enabled, the salt used will be obtained via the {@link #getSalt(AuthenticationToken) getSalt} method.
219 * <p/>
220 * The default value is {@code false}.
221 *
222 * @return {@code true} if a submitted {@code AuthenticationToken}'s credentials should be salted when hashing,
223 * {@code false} if it should not be salted.
224 * @deprecated since Shiro 1.1. Hash salting is now expected to be based on if the {@link AuthenticationInfo}
225 * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its
226 * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method
227 * returns a non-null value.
228 * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return
229 * {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations
230 * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo}
231 * instances as soon as possible</b>.
232 * <p/>
233 * This is because salts should always be obtained from the stored account information and
234 * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for
235 * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user
236 * are almost impossible to break. This method will be removed in Shiro 2.0.
237 */
238 @Deprecated
239 public boolean isHashSalted() {
240 return hashSalted;
241 }
242
243 /**
244 * Sets whether to salt a submitted {@code AuthenticationToken}'s credentials when hashing.
245 * <p/>
246 * If enabled, the salt used will be obtained via the
247 * {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getCredentialsSalt} method.
248 * </p>
249 * The default value is {@code false}.
250 *
251 * @param hashSalted whether to salt a submitted {@code AuthenticationToken}'s credentials when hashing.
252 * @deprecated since Shiro 1.1. Hash salting is now expected to be based on if the {@link AuthenticationInfo}
253 * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its
254 * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()}
255 * method returns a non-null value.
256 * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return
257 * {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations
258 * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo}
259 * instances as soon as possible</b>.
260 * <p/>
261 * This is because salts should always be obtained from the stored account information and
262 * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for
263 * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user
264 * are almost impossible to break. This method will be removed in Shiro 2.0.
265 */
266 @Deprecated
267 public void setHashSalted(boolean hashSalted) {
268 this.hashSalted = hashSalted;
269 }
270
271 /**
272 * Returns the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before
273 * comparing to the credentials stored in the system.
274 * <p/>
275 * Unless overridden, the default value is {@code 1}, meaning a normal hash execution will occur.
276 *
277 * @return the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before
278 * comparing to the credentials stored in the system.
279 */
280 public int getHashIterations() {
281 return hashIterations;
282 }
283
284 /**
285 * Sets the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before comparing
286 * to the credentials stored in the system.
287 * <p/>
288 * Unless overridden, the default value is {@code 1}, meaning a normal single hash execution will occur.
289 * <p/>
290 * If this argument is less than 1 (i.e. 0 or negative), the default value of 1 is applied. There must always be
291 * at least 1 hash iteration (otherwise there would be no hash).
292 *
293 * @param hashIterations the number of times to hash a submitted {@code AuthenticationToken}'s credentials.
294 */
295 public void setHashIterations(int hashIterations) {
296 this.hashIterations = Math.max(hashIterations, 1);
297 }
298
299 /**
300 * Returns a salt value used to hash the token's credentials.
301 * <p/>
302 * This default implementation merely returns {@code token.getPrincipal()}, effectively using the user's
303 * identity (username, user id, etc.) as the salt, a most common technique. If you wish to provide the
304 * authentication token's salt another way, you may override this method.
305 *
306 * @param token the AuthenticationToken submitted during the authentication attempt.
307 * @return a salt value to use to hash the authentication token's credentials.
308 * @deprecated since Shiro 1.1. Hash salting is now expected to be based on if the {@link AuthenticationInfo}
309 * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its
310 * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method
311 * returns a non-null value.
312 * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return
313 * {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations
314 * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo}
315 * instances as soon as possible</b>.<p/>
316 * This is because salts should always be obtained from the stored account information and
317 * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for
318 * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user
319 * are almost impossible to break. This method will be removed in Shiro 2.0.
320 */
321 @Deprecated
322 protected Object getSalt(AuthenticationToken token) {
323 return token.getPrincipal();
324 }
325
326 /**
327 * Returns a {@link Hash Hash} instance representing the already-hashed AuthenticationInfo credentials stored in the system.
328 * <p/>
329 * This method reconstructs a {@link Hash Hash} instance based on a {@code info.getCredentials} call,
330 * but it does <em>not</em> hash that value - it is expected that method call will return an already-hashed value.
331 * <p/>
332 * This implementation's reconstruction effort functions as follows:
333 * <ol>
334 * <li>Convert {@code account.getCredentials()} to a byte array via the {@link #toBytes toBytes} method.
335 * <li>If {@code account.getCredentials()} was originally a String or char[] before {@code toBytes} was
336 * called, check for encoding:
337 * <li>If {@link #storedCredentialsHexEncoded storedCredentialsHexEncoded}, Hex decode that byte array, otherwise
338 * Base64 decode the byte array</li>
339 * <li>Set the byte[] array directly on the {@code Hash} implementation and return it.</li>
340 * </ol>
341 *
342 * @param info the AuthenticationInfo from which to retrieve the credentials which assumed to be in already-hashed form.
343 * @return a {@link Hash Hash} instance representing the given AuthenticationInfo's stored credentials.
344 */
345 @Override
346 protected Object getCredentials(AuthenticationInfo info) {
347 Object credentials = info.getCredentials();
348
349 byte[] storedBytes = toBytes(credentials);
350
351 if (credentials instanceof String || credentials instanceof char[]) {
352 //account.credentials were a char[] or String, so
353 //we need to do text decoding first:
354 if (isStoredCredentialsHexEncoded()) {
355 storedBytes = Hex.decode(storedBytes);
356 } else {
357 storedBytes = Base64.decode(storedBytes);
358 }
359 }
360 SimpleHash hash = newHashInstance();
361 hash.setBytes(storedBytes);
362 return hash;
363 }
364
365 /**
366 * This implementation first hashes the {@code token}'s credentials, potentially using a
367 * {@code salt} if the {@code info} argument is a
368 * {@link org.apache.shiro.authc.SaltedAuthenticationInfo SaltedAuthenticationInfo}. It then compares the hash
369 * against the {@code AuthenticationInfo}'s
370 * {@link #getCredentials(org.apache.shiro.authc.AuthenticationInfo) already-hashed credentials}. This method
371 * returns {@code true} if those two values are {@link #equals(Object, Object) equal}, {@code false} otherwise.
372 *
373 * @param token the {@code AuthenticationToken} submitted during the authentication attempt.
374 * @param info the {@code AuthenticationInfo} stored in the system matching the token principal
375 * @return {@code true} if the provided token credentials hash match to the stored account credentials hash,
376 * {@code false} otherwise
377 * @since 1.1
378 */
379 @Override
380 public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
381 Object tokenHashedCredentials = hashProvidedCredentials(token, info);
382 Object accountCredentials = getCredentials(info);
383 return equals(tokenHashedCredentials, accountCredentials);
384 }
385
386 /**
387 * Hash the provided {@code token}'s credentials using the salt stored with the account if the
388 * {@code info} instance is an {@code instanceof} {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} (see
389 * the class-level JavaDoc for why this is the preferred approach).
390 * <p/>
391 * If the {@code info} instance is <em>not</em>
392 * an {@code instanceof} {@code SaltedAuthenticationInfo}, the logic will fall back to Shiro 1.0
393 * backwards-compatible logic: it will first check to see {@link #isHashSalted() isHashSalted} and if so, will try
394 * to acquire the salt from {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)}. See the class-level
395 * JavaDoc for why this is not recommended. This 'fallback' logic exists only for backwards-compatibility.
396 * {@code Realm}s should be updated as soon as possible to return {@code SaltedAuthenticationInfo} instances
397 * if account credentials salting is enabled (highly recommended for password-based systems).
398 *
399 * @param token the submitted authentication token from which its credentials will be hashed
400 * @param info the stored account data, potentially used to acquire a salt
401 * @return the token credentials hash
402 * @since 1.1
403 */
404 protected Object hashProvidedCredentials(AuthenticationToken token, AuthenticationInfo info) {
405 final Object salt;
406 if (info instanceof SaltedAuthenticationInfo) {
407 salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
408 } else if (isHashSalted()) {
409 //retain 1.0 backwards compatibility:
410 salt = getSalt(token);
411 } else {
412 salt = SimpleByteSource.empty();
413 }
414 return hashProvidedCredentials(token.getCredentials(), salt, getHashIterations());
415 }
416
417 /**
418 * Returns the {@link #getHashAlgorithmName() hashAlgorithmName} property, but will throw an
419 * {@link IllegalStateException} if it has not been set.
420 *
421 * @return the required {@link #getHashAlgorithmName() hashAlgorithmName} property
422 * @throws IllegalStateException if the property has not been set prior to calling this method.
423 * @since 1.1
424 */
425 private String assertHashAlgorithmName() throws IllegalStateException {
426 String hashAlgorithmName = getHashAlgorithmName();
427 if (hashAlgorithmName == null) {
428 String msg = "Required 'hashAlgorithmName' property has not been set. This is required to execute "
429 + "the hashing algorithm.";
430 throw new IllegalStateException(msg);
431 }
432 return hashAlgorithmName;
433 }
434
435 /**
436 * Hashes the provided credentials a total of {@code hashIterations} times, using the given salt. The hash
437 * implementation/algorithm used is based on the {@link #getHashAlgorithmName() hashAlgorithmName} property.
438 *
439 * @param credentials the submitted authentication token's credentials to hash
440 * @param salt the value to salt the hash. Cannot be {@code null}, but an empty ByteSource.
441 * @param hashIterations the number of times to hash the credentials. At least one hash will always occur though,
442 * even if this argument is 0 or negative.
443 * @return the hashed value of the provided credentials, according to the specified salt and hash iterations.
444 * @throws NullPointerException if salt is {@code null}.
445 */
446 protected Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations) {
447 String hashAlgorithmName = assertHashAlgorithmName();
448 return new SimpleHash(hashAlgorithmName, credentials, requireNonNull(salt, "salt cannot be null."), hashIterations);
449 }
450
451 /**
452 * Returns a new, <em>uninitialized</em> instance, without its byte array set. Used as a utility method in the
453 * {@link SimpleCredentialsMatcher#getCredentials(org.apache.shiro.authc.AuthenticationInfo)
454 * getCredentials(AuthenticationInfo)} implementation.
455 *
456 * @return a new, <em>uninitialized</em> instance, without its byte array set.
457 */
458 protected SimpleHash newHashInstance() {
459 String hashAlgorithmName = assertHashAlgorithmName();
460 return new SimpleHash(hashAlgorithmName);
461 }
462
463 }