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