View Javadoc
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   * &quot;Why add salt?&quot; 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  * &quot;Hardening against the attacker's attack&quot;</a> section to learn more about why you might want to use
106  * multiple hash iterations.
107  * <h2>MD5 &amp; 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 }