001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.shiro.authc.credential;
020    
021    import org.apache.shiro.authc.AuthenticationInfo;
022    import org.apache.shiro.authc.AuthenticationToken;
023    import org.apache.shiro.authc.SaltedAuthenticationInfo;
024    import org.apache.shiro.codec.Base64;
025    import org.apache.shiro.codec.Hex;
026    import org.apache.shiro.crypto.hash.AbstractHash;
027    import org.apache.shiro.crypto.hash.Hash;
028    import org.apache.shiro.crypto.hash.SimpleHash;
029    import org.apache.shiro.util.StringUtils;
030    
031    /**
032     * A {@code HashedCredentialMatcher} provides support for hashing of supplied {@code AuthenticationToken} credentials
033     * before being compared to those in the {@code AuthenticationInfo} from the data store.
034     * <p/>
035     * Credential hashing is one of the most common security techniques when safeguarding a user's private credentials
036     * (passwords, keys, etc).  Most developers never want to store their users' credentials in plain form, viewable by
037     * anyone, so they often hash the users' credentials before they are saved in the data store.
038     * <p/>
039     * This class (and its subclasses) function as follows:
040     * <ol>
041     * <li>Hash the {@code AuthenticationToken} credentials supplied by the user during their login.</li>
042     * <li>Compare this hashed value directly with the {@code AuthenticationInfo} credentials stored in the system
043     * (the stored account credentials are expected to already be in hashed form).</li>
044     * <li>If these two values are {@link #equals(Object, Object) equal}, the submitted credentials match, otherwise
045     * they do not.</li>
046     * </ol>
047     * <h2>Salting and Multiple Hash Iterations</h2>
048     * Because simple hashing is usually not good enough for secure applications, this class also supports 'salting'
049     * and multiple hash iterations.  Please read this excellent
050     * <a href="http://www.owasp.org/index.php/Hashing_Java" _target="blank">Hashing Java article</a> to learn about
051     * salting and multiple iterations and why you might want to use them. (Note of sections 5
052     * &quot;Why add salt?&quot; and 6 "Hardening against the attacker's attack").   We should also note here that all of
053     * Shiro's Hash implementations (for example, {@link org.apache.shiro.crypto.hash.Md5Hash Md5Hash},
054     * {@link org.apache.shiro.crypto.hash.Sha1Hash Sha1Hash}, etc) support salting and multiple hash iterations via
055     * overloaded constructors.
056     * <h4>Real World Case Study</h4>
057     * In April 2010, some public Atlassian Jira and Confluence
058     * installations (Apache Software Foundation, Codehaus, etc) were the target of account attacks and user accounts
059     * were compromised.  The reason?  Jira and Confluence at the time did not salt user passwords and attackers were
060     * able to use dictionary attacks to compromise user accounts (Atlassian has since
061     * <a href="http://blogs.atlassian.com/news/2010/04/oh_man_what_a_day_an_update_on_our_security_breach.html">
062     * fixed the problem</a> of course).
063     * <p/>
064     * The lesson?
065     * <p/>
066     * <b>ALWAYS, ALWAYS, ALWAYS SALT USER PASSWORDS!</b>
067     * <p/>
068     * <h3>Salting</h3>
069     * Prior to Shiro 1.1, salts could be obtained based on the end-user submitted
070     * {@link AuthenticationToken AuthenticationToken} via the now-deprecated
071     * {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getSalt(AuthenticationToken)} method.  This however
072     * could constitute a security hole since ideally salts should never be obtained based on what a user can submit.
073     * User-submitted salt mechanisms are <em>much</em> more susceptible to dictionary attacks and <b>SHOULD NOT</b> be
074     * used in secure systems.  Instead salts should ideally be a secure randomly-generated number that is generated when
075     * the user account is created.  The secure number should never be disseminated to the user and always kept private
076     * by the application.
077     * <h4>Shiro 1.1</h4>
078     * As of Shiro 1.1, it is expected that any salt used to hash the submitted credentials will be obtained from the
079     * stored account information (represented as an {@link AuthenticationInfo AuthenticationInfo} instance).  This is much
080     * more secure because the salt value remains private to the application (Shiro will never store this value).
081     * <p/>
082     * To enable this, {@code Realm}s should return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances
083     * during authentication.  {@code HashedCredentialsMatcher} implementations will then use the provided
084     * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt credentialsSalt} for hashing.  To avoid
085     * security risks,
086     * <b>it is highly recommended that any existing {@code Realm} implementations that support hashed credentials are
087     * updated to return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances as soon as possible</b>.
088     * <h4>Shiro 1.0 Backwards Compatibility</h4>
089     * Because of the identified security risk, {@code Realm} implementations that support credentials hashing should
090     * be updated to return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances as
091     * soon as possible.
092     * <p/>
093     * If this is not possible for some reason, this class will retain 1.0 backwards-compatible behavior of obtaining
094     * the salt via the now-deprecated {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)} method.  This
095     * method will only be invoked if a {@code Realm} <em>does not</em> return
096     * {@link SaltedAuthenticationInfo SaltedAutenticationInfo} instances and {@link #isHashSalted() hashSalted} is
097     * {@code true}.
098     * But please note that the {@link #isHashSalted() hashSalted} property and the
099     * {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)} methods will be removed before the Shiro 2.0
100     * release.
101     * <h3>Multiple Hash Iterations</h3>
102     * If you hash your users' credentials multiple times before persisting to the data store, you will also need to
103     * set this class's {@link #setHashIterations(int) hashIterations} property.  See the
104     * <a href="http://www.owasp.org/index.php/Hashing_Java" _target="blank">Hashing Java article</a>'s
105     * <a href="http://www.owasp.org/index.php/Hashing_Java#Hardening_against_the_attacker.27s_attack">
106     * &quot;Hardening against the attacker's attack&quot;</a> section to learn more about why you might want to use
107     * multiple hash iterations.
108     * <h2>MD5 &amp; SHA-1 Notice</h2>
109     * <a href="http://en.wikipedia.org/wiki/MD5">MD5</a> and
110     * <a href="http://en.wikipedia.org/wiki/SHA_hash_functions">SHA-1</a> algorithms are now known to be vulnerable to
111     * compromise and/or collisions (read the linked pages for more).  While most applications are ok with either of these
112     * two, if your application mandates high security, use the SHA-256 (or higher) hashing algorithms and their
113     * supporting {@code CredentialsMatcher} implementations.
114     *
115     * @see org.apache.shiro.crypto.hash.Md5Hash
116     * @see org.apache.shiro.crypto.hash.Sha1Hash
117     * @see org.apache.shiro.crypto.hash.Sha256Hash
118     * @since 0.9
119     */
120    public class HashedCredentialsMatcher extends SimpleCredentialsMatcher {
121    
122        /**
123         * @since 1.1
124         */
125        private String hashAlgorithm;
126        private int hashIterations;
127        private boolean hashSalted;
128        private boolean storedCredentialsHexEncoded;
129    
130        /**
131         * JavaBeans-compatibile no-arg constructor intended for use in IoC/Dependency Injection environments.  If you
132         * use this constructor, you <em>MUST</em> also additionally set the
133         * {@link #setHashAlgorithmName(String) hashAlgorithmName} property.
134         */
135        public HashedCredentialsMatcher() {
136            this.hashAlgorithm = null;
137            this.hashSalted = false;
138            this.hashIterations = 1;
139            this.storedCredentialsHexEncoded = true; //false means Base64-encoded
140        }
141    
142        /**
143         * Creates an instance using the specified {@link #getHashAlgorithmName() hashAlgorithmName} to hash submitted
144         * credentials.
145         * @param hashAlgorithmName the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName}
146         *                          to use when performing hashes for credentials matching.
147         * @since 1.1
148         */
149        public HashedCredentialsMatcher(String hashAlgorithmName) {
150            this();
151            if (!StringUtils.hasText(hashAlgorithmName) ) {
152                throw new IllegalArgumentException("hashAlgorithmName cannot be null or empty.");
153            }
154            this.hashAlgorithm = hashAlgorithmName;
155        }
156    
157        /**
158         * Returns the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} to use
159         * when performing hashes for credentials matching.
160         *
161         * @return the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} to use
162         *         when performing hashes for credentials matching.
163         * @since 1.1
164         */
165        public String getHashAlgorithmName() {
166            return hashAlgorithm;
167        }
168    
169        /**
170         * Sets the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} to use
171         * when performing hashes for credentials matching.
172         *
173         * @param hashAlgorithmName the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName}
174         *                          to use when performing hashes for credentials matching.
175         * @since 1.1
176         */
177        public void setHashAlgorithmName(String hashAlgorithmName) {
178            this.hashAlgorithm = hashAlgorithmName;
179        }
180    
181        /**
182         * Returns {@code true} if the system's stored credential hash is Hex encoded, {@code false} if it
183         * is Base64 encoded.
184         * <p/>
185         * Default value is {@code true} for convenience - all of Shiro's {@link Hash Hash#toString()}
186         * implementations return Hex encoded values by default, making this class's use with those implementations
187         * easier.
188         *
189         * @return {@code true} if the system's stored credential hash is Hex encoded, {@code false} if it
190         *         is Base64 encoded.  Default is {@code true}
191         */
192        public boolean isStoredCredentialsHexEncoded() {
193            return storedCredentialsHexEncoded;
194        }
195    
196        /**
197         * Sets the indicator if this system's stored credential hash is Hex encoded or not.
198         * <p/>
199         * A value of {@code true} will cause this class to decode the system credential from Hex, a
200         * value of {@code false} will cause this class to decode the system credential from Base64.
201         * <p/>
202         * Unless overridden via this method, the default value is {@code true} for convenience - all of Shiro's
203         * {@link Hash Hash#toString()} implementations return Hex encoded values by default, making this class's use with
204         * those implementations easier.
205         *
206         * @param storedCredentialsHexEncoded the indicator if this system's stored credential hash is Hex
207         *                                    encoded or not ('not' automatically implying it is Base64 encoded).
208         */
209        public void setStoredCredentialsHexEncoded(boolean storedCredentialsHexEncoded) {
210            this.storedCredentialsHexEncoded = storedCredentialsHexEncoded;
211        }
212    
213        /**
214         * Returns {@code true} if a submitted {@code AuthenticationToken}'s credentials should be salted when hashing,
215         * {@code false} if it should not be salted.
216         * <p/>
217         * If enabled, the salt used will be obtained via the {@link #getSalt(AuthenticationToken) getSalt} method.
218         * <p/>
219         * The default value is {@code false}.
220         *
221         * @return {@code true} if a submitted {@code AuthenticationToken}'s credentials should be salted when hashing,
222         *         {@code false} if it should not be salted.
223         * @deprecated since Shiro 1.1.  Hash salting is now expected to be based on if the {@link AuthenticationInfo}
224         *             returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its
225         *             {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value.
226         *             This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return
227         *             {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations
228         *             that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo}
229         *             instances as soon as possible</b>.
230         *             <p/>
231         *             This is because salts should always be obtained from the stored account information and
232         *             never be interpreted based on user/Subject-entered data.  User-entered data is easier to compromise for
233         *             attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user
234         *             are almost impossible to break.  This method will be removed in Shiro 2.0.
235         */
236        @Deprecated
237        public boolean isHashSalted() {
238            return hashSalted;
239        }
240    
241        /**
242         * Sets whether or not to salt a submitted {@code AuthenticationToken}'s credentials when hashing.
243         * <p/>
244         * If enabled, the salt used will be obtained via the {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getCredentialsSalt} method.
245         * </p>
246         * The default value is {@code false}.
247         *
248         * @param hashSalted whether or not to salt a submitted {@code AuthenticationToken}'s credentials when hashing.
249         * @deprecated since Shiro 1.1.  Hash salting is now expected to be based on if the {@link AuthenticationInfo}
250         *             returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its
251         *             {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value.
252         *             This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return
253         *             {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations
254         *             that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo}
255         *             instances as soon as possible</b>.
256         *             <p/>
257         *             This is because salts should always be obtained from the stored account information and
258         *             never be interpreted based on user/Subject-entered data.  User-entered data is easier to compromise for
259         *             attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user
260         *             are almost impossible to break.  This method will be removed in Shiro 2.0.
261         */
262        @Deprecated
263        public void setHashSalted(boolean hashSalted) {
264            this.hashSalted = hashSalted;
265        }
266    
267        /**
268         * Returns the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before
269         * comparing to the credentials stored in the system.
270         * <p/>
271         * Unless overridden, the default value is {@code 1}, meaning a normal hash execution will occur.
272         *
273         * @return the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before
274         *         comparing to the credentials stored in the system.
275         */
276        public int getHashIterations() {
277            return hashIterations;
278        }
279    
280        /**
281         * Sets the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before comparing
282         * to the credentials stored in the system.
283         * <p/>
284         * Unless overridden, the default value is {@code 1}, meaning a normal single hash execution will occur.
285         * <p/>
286         * If this argument is less than 1 (i.e. 0 or negative), the default value of 1 is applied.  There must always be
287         * at least 1 hash iteration (otherwise there would be no hash).
288         *
289         * @param hashIterations the number of times to hash a submitted {@code AuthenticationToken}'s credentials.
290         */
291        public void setHashIterations(int hashIterations) {
292            if (hashIterations < 1) {
293                this.hashIterations = 1;
294            } else {
295                this.hashIterations = hashIterations;
296            }
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 returns a non-null value.
311         *             This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return
312         *             {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations
313         *             that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo}
314         *             instances as soon as possible</b>.<p/>
315         *             This is because salts should always be obtained from the stored account information and
316         *             never be interpreted based on user/Subject-entered data.  User-entered data is easier to compromise for
317         *             attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user
318         *             are almost impossible to break.  This method will be removed in Shiro 2.0.
319         */
320        @Deprecated
321        protected Object getSalt(AuthenticationToken token) {
322            return token.getPrincipal();
323        }
324    
325        /**
326         * Returns a {@link Hash Hash} instance representing the already-hashed AuthenticationInfo credentials stored in the system.
327         * <p/>
328         * This method reconstructs a {@link Hash Hash} instance based on a {@code info.getCredentials} call,
329         * but it does <em>not</em> hash that value - it is expected that method call will return an already-hashed value.
330         * <p/>
331         * This implementation's reconstruction effort functions as follows:
332         * <ol>
333         * <li>Convert {@code account.getCredentials()} to a byte array via the {@link #toBytes toBytes} method.
334         * <li>If {@code account.getCredentials()} was originally a String or char[] before {@code toBytes} was
335         * called, check for encoding:
336         * <li>If {@link #storedCredentialsHexEncoded storedCredentialsHexEncoded}, Hex decode that byte array, otherwise
337         * Base64 decode the byte array</li>
338         * <li>Set the byte[] array directly on the {@code Hash} implementation and return it.</li>
339         * </ol>
340         *
341         * @param info the AuthenticationInfo from which to retrieve the credentials which assumed to be in already-hashed form.
342         * @return a {@link Hash Hash} instance representing the given AuthenticationInfo's stored credentials.
343         */
344        protected Object getCredentials(AuthenticationInfo info) {
345            Object credentials = info.getCredentials();
346    
347            byte[] storedBytes = toBytes(credentials);
348    
349            if (credentials instanceof String || credentials instanceof char[]) {
350                //account.credentials were a char[] or String, so
351                //we need to do text decoding first:
352                if (isStoredCredentialsHexEncoded()) {
353                    storedBytes = Hex.decode(storedBytes);
354                } else {
355                    storedBytes = Base64.decode(storedBytes);
356                }
357            }
358            AbstractHash hash = newHashInstance();
359            hash.setBytes(storedBytes);
360            return hash;
361        }
362    
363        /**
364         * This implementation first hashes the {@code token}'s credentials, potentially using a
365         * {@code salt} if the {@code info} argument is a
366         * {@link org.apache.shiro.authc.SaltedAuthenticationInfo SaltedAuthenticationInfo}.  It then compares the hash
367         * against the {@code AuthenticationInfo}'s
368         * {@link #getCredentials(org.apache.shiro.authc.AuthenticationInfo) already-hashed credentials}.  This method
369         * returns {@code true} if those two values are {@link #equals(Object, Object) equal}, {@code false} otherwise.
370         *
371         * @param token the {@code AuthenticationToken} submitted during the authentication attempt.
372         * @param info  the {@code AuthenticationInfo} stored in the system matching the token principal
373         * @return {@code true} if the provided token credentials hash match to the stored account credentials hash,
374         *         {@code false} otherwise
375         * @since 1.1
376         */
377        @Override
378        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
379            Object tokenHashedCredentials = hashProvidedCredentials(token, info);
380            Object accountCredentials = getCredentials(info);
381            return equals(tokenHashedCredentials, accountCredentials);
382        }
383    
384        /**
385         * Hash the provided {@code token}'s credentials using the salt stored with the account if the
386         * {@code info} instance is an {@code instanceof} {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} (see
387         * the class-level JavaDoc for why this is the preferred approach).
388         * <p/>
389         * If the {@code info} instance is <em>not</em>
390         * an {@code instanceof} {@code SaltedAuthenticationInfo}, the logic will fall back to Shiro 1.0
391         * backwards-compatible logic:  it will first check to see {@link #isHashSalted() isHashSalted} and if so, will try
392         * to acquire the salt from {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)}.  See the class-level
393         * JavaDoc for why this is not recommended.  This 'fallback' logic exists only for backwards-compatibility.
394         * {@code Realm}s should be updated as soon as possible to return {@code SaltedAuthenticationInfo} instances
395         * if account credentials salting is enabled (highly recommended for password-based systems).
396         *
397         * @param token the submitted authentication token from which its credentials will be hashed
398         * @param info  the stored account data, potentially used to acquire a salt
399         * @return the token credentials hash
400         * @since 1.1
401         */
402        protected Object hashProvidedCredentials(AuthenticationToken token, AuthenticationInfo info) {
403            Object salt = null;
404            if (info instanceof SaltedAuthenticationInfo) {
405                salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
406            } else {
407                //retain 1.0 backwards compatibility:
408                if (isHashSalted()) {
409                    salt = getSalt(token);
410                }
411            }
412            return hashProvidedCredentials(token.getCredentials(), salt, getHashIterations());
413        }
414    
415        /**
416         * Returns the {@link #getHashAlgorithmName() hashAlgorithmName} property, but will throw an
417         * {@link IllegalStateException} if it has not been set.
418         *
419         * @return the required {@link #getHashAlgorithmName() hashAlgorithmName} property
420         * @throws IllegalStateException if the property has not been set prior to calling this method.
421         * @since 1.1
422         */
423        private String assertHashAlgorithmName() throws IllegalStateException {
424            String hashAlgorithmName = getHashAlgorithmName();
425            if (hashAlgorithmName == null) {
426                String msg = "Required 'hashAlgorithmName' property has not been set.  This is required to execute " +
427                        "the hashing algorithm.";
428                throw new IllegalStateException(msg);
429            }
430            return hashAlgorithmName;
431        }
432    
433        /**
434         * Hashes the provided credentials a total of {@code hashIterations} times, using the given salt.  The hash
435         * implementation/algorithm used is based on the {@link #getHashAlgorithmName() hashAlgorithmName} property.
436         *
437         * @param credentials    the submitted authentication token's credentials to hash
438         * @param salt           the value to salt the hash, or {@code null} if a salt will not be used.
439         * @param hashIterations the number of times to hash the credentials.  At least one hash will always occur though,
440         *                       even if this argument is 0 or negative.
441         * @return the hashed value of the provided credentials, according to the specified salt and hash iterations.
442         */
443        protected Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations) {
444            String hashAlgorithmName = assertHashAlgorithmName();
445            return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
446        }
447    
448        /**
449         * Returns a new, <em>uninitialized</em> instance, without its byte array set.  Used as a utility method in the
450         * {@link SimpleCredentialsMatcher#getCredentials(org.apache.shiro.authc.AuthenticationInfo) getCredentials(AuthenticationInfo)} implementation.
451         *
452         * @return a new, <em>uninitialized</em> instance, without its byte array set.
453         */
454        protected AbstractHash newHashInstance() {
455            String hashAlgorithmName = assertHashAlgorithmName();
456            return new SimpleHash(hashAlgorithmName);
457        }
458    
459    }