[jira] [Commented] (SHIRO-552) JdbcRealm in SaltStyle.COLUMN assumes that password column is Base64 but salt column is utf8 bytes

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view

[jira] [Commented] (SHIRO-552) JdbcRealm in SaltStyle.COLUMN assumes that password column is Base64 but salt column is utf8 bytes

JIRA jira@apache.org

    [ https://issues.apache.org/jira/browse/SHIRO-552?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16053244#comment-16053244 ]

Steinar Bang commented on SHIRO-552:

I've been trying to convert my existing salt to a form understood by the JdbcRealm but so far I've failed:
I can't get the same byte[] out of CodecSupport.toBytes(String) that I'm putting in.

Has anyone successfully used the JdbcRealm with salt?

So far I haven't found any google matches that shows working code examples of this.  The closest I've been getting is [this stackoverflow answer|https://stackoverflow.com/a/23755827], which has saving the salt as a TODO

This is what I've done to try to fix the salt in the database:
# Add a liquibase changeset that adds a new column to the users table in the database
    <changeSet author="sb" id="add-password-salt-column-to-users-table">
        <addColumn tableName="users">
            <column name="password_salt" type="varchar(255)" />
# Add a liquibase changeset that runs a custom task to convert the old base64 encoded salt values to utf-8 encoded salt values
    <changeSet author="sb" id="convert-salt-from-base64-to-utf8" runAlways="false" failOnError="true>
        <customChange class="no.priv.bang.ukelonn.bundle.db.liquibase.customchange.ConvertUsersTableSaltEncodingFromBase64ToUtf8" />
The essential part of the custom task, is:
public class ConvertUsersTableSaltEncodingFromBase64ToUtf8 implements CustomTaskChange {

        public void execute(Database database) throws CustomChangeException {
            JdbcConnection connection = (JdbcConnection) database.getConnection();
            try {
                PreparedStatement getsalt = connection.prepareStatement("select user_id, salt from users");
                PreparedStatement updateUtf8EncodedSalt = connection.prepareStatement("update users set password_salt=? where user_id=?");
                ResultSet result = getsalt.executeQuery();
                while(result.next()) {
                    int userId = result.getInt(0);
                    String base64EncodedSalt = result.getString(1);
                    byte[] decodedSalt = Base64.getDecoder().decode(base64EncodedSalt);
                    char[] utf8EncodedSalt = CodecSupport.toChars(decodedSalt);
                //String utf8EncodedSalt = CodecSupport.toString(decodedSalt);
                //String utf8EncodedSalt = new String(decodedSalt, StandardCharsets.UTF_8);
                    updateUtf8EncodedSalt.setString(0, new String(utf8EncodedSalt));
                    updateUtf8EncodedSalt.setInt(1, userId);
        } catch (Exception e) {
            throw new CustomChangeException("Failed to convert users table salt values from base64 to utf-8", e);

The variuous commented out variants all give the same results:
What I put in, is:
[34, -17, 112, -83, -19, -119, -127, 123, -79, -67, 20, -10, 48, 13, 2, 25]
What I get out, is:
[34, -17, -65, -67, 112, -17, -65, -67, -19, -119, -127, 123, -17, -65, -67, -17, -65, -67, 20, -17, -65, -67, 48, 13, 2, 25]

There is a pattern there: byte three in the original sequence can be found as byte 5 in the second, the start and end are the same in both sequences

But trying to figure out what the correct way of encoding the salt should be has so far evaded me...

Base64 would have been much simpler, and I know from my own custom realm: it works
(and when I think about it: the issues around the salt may be why I ended up with a custom realm last autumn...? I remember something vaguely about that)

> JdbcRealm in SaltStyle.COLUMN assumes that password column is Base64 but salt column is utf8 bytes
> --------------------------------------------------------------------------------------------------
>                 Key: SHIRO-552
>                 URL: https://issues.apache.org/jira/browse/SHIRO-552
>             Project: Shiro
>          Issue Type: Bug
>    Affects Versions: 1.2.4
>            Reporter: Richard Bradley
> The {{org.apache.shiro.realm.jdbc.JdbcRealm}} class, when configured with SaltStyle.COLUMN, assumes that password column is Base64 but salt column is utf8 bytes.
> The password is returned as a {{char[]}} (see JdbcRealm.java:241), which {{org.apache.shiro.authc.credential.HashedCredentialsMatcher}} (see HashedCredentialsMatcher.java:353):
> {code}
>         if (credentials instanceof String || credentials instanceof char[]) {
>             //account.credentials were a char[] or String, so
>             //we need to do text decoding first:
>             if (isStoredCredentialsHexEncoded()) {
>                 storedBytes = Hex.decode(storedBytes);
>             } else {
>                 storedBytes = Base64.decode(storedBytes);
>             }
>         }
> {code}
> However, the salt is returned as a {{ByteSource}}, by converting the DB-returned String into its UTF-8 bytes. See JdbcRealm.java:224:
> {code}
>             if (salt != null) {
>                 info.setCredentialsSalt(ByteSource.Util.bytes(salt));
>             }
> {code}
> This is broken and inconsistent.
> Not all salt byte[]s are valid UTF8 strings, so the default assumption should be that the salt column is Base64 encoded.

This message was sent by Atlassian JIRA