NAME
DBIO::EncodedColumn - One-way encode selected columns: passwords with Bcrypt or Argon2, tokens with Digest
VERSION
version 0.900004
SYNOPSIS
package MyApp::Schema::Result::User;
use base 'DBIO::Core';
__PACKAGE__->load_components(qw/EncodedColumn/);
__PACKAGE__->add_columns(
password => {
data_type => 'varchar',
size => 255,
encode_column => 1,
encode_check_method => 'check_password',
},
api_token => {
data_type => 'varchar',
size => 255,
encode_column => 1,
encode_class => 'Digest',
encode_args => { algorithm => 'SHA-256' },
},
);
my $user = $schema->resultset('User')->new_result({ password => 's3cret' });
$user->check_password('s3cret'); # true
See t/94encoded_column.t for a runnable example.
DESCRIPTION
DBIO::EncodedColumn implements one-way column encoding. Columns are marked with encode_column => 1 in add_columns.
The value is encoded on the way to storage. Reading a column returns the stored (encoded) representation. A verifier method per encoded column is installed.
Classes
encode_class chooses the method:
Bcrypt(default)-
Passwords. Crypt::Bcrypt, variant
2b,cost12 unlessencode_argssays otherwise. The stored value is the$2b$...string. bcrypt takes values of up to 72 bytes without a NUL byte; a longer value or one with a NUL byte is refused with an exception that points toArgon2. Argon2-
Passwords. Crypt::Argon2, argon2id, with
t_cost,m_costandparallelismfromencode_args. The stored value is the$argon2id$...string. Digest-
Tokens and values that are compared by their digest. A salted Digest of SHA-256, SHA-384, SHA-512 or SHA3-224, SHA3-256, SHA3-384, SHA3-512, stored as
dbio$<algorithm>$<salt_base64>$<digest_base64>
The module of the chosen class has to be installed: a class whose module is missing does not load, and the exception names the module.
Every value goes into the method as the bytes of its UTF-8 encoding, when it is written and when it is checked.
What is encoded and what passes through
A value handed to an encoded column through the accessor, new, create or update is encoded, whatever it looks like: a defined value that is neither a reference nor a literal SQL value is always taken as a password. That includes a value that only looks like a hash ($2a$my-own-password, dbio$a$b$c) and the value read from the row itself -- $row->password($row->password) encodes the stored hash a second time. To leave a stored value as it is, do not set it, or use set_column. copy carries the stored value over as it is.
Storing a finished hash
A hash made elsewhere (importing accounts with their hashes) goes past the filter explicitly, with set_column, which the filter does not act on, or with a literal in new/create:
$user->set_column(password => $hash);
$schema->resultset('User')->create({
name => 'alice',
password => \[ '?', $hash ],
});
check_password verifies such a value like any stored one.
Verification
The stored value names its method and the check uses exactly that one: $2...$ values (bcrypt) are checked with Crypt::Bcrypt, $argon2id$... values with Crypt::Argon2, dbio$... values with the algorithm the value names (a name this installation cannot build gives 0; digests of MD5 and SHA-1 that exist in a column stay checkable). The module a check needs has to be installed; without it the check method throws an exception naming the module. A candidate that bcrypt cannot take (over 72 bytes, or with a NUL byte) gives 0.
A stored value that has no hash format at all never matches (accept_unencoded is 0). With accept_unencoded => 1 in encode_args a stored value that equals the candidate matches (compared in constant time, answer 1 or 0) and is written back as a hash of the column's class: the check method updates this one column of the row, only while the database still holds the value that was compared, and warns once. A row that is not in the database yet gets the hash as its column value. A value that claims a hash format but is not a valid one never matches, in either setting.
Serializing and the changelog
A column with encode_column has is_serializable => 0 (it is not part of TO_JSON) and changelog => 0 (DBIO::ChangeLog leaves it out), unless the column definition says otherwise.
Trace
The bind values of an encoded column appear as *** in the SQL trace; see "redact_bind_value" in DBIO::Storage::DBI.
METHODS
add_columns
Component override: gives every column with encode_column the column settings is_serializable => 0 and changelog => 0 unless the definition names them, then delegates to next::method. The changelog setting is handed on as the marker DBIO::ChangeLog reads, so it holds in either order of load_components. Called the same way as the plain add_columns; not normally called directly.
register_column
Component override: after delegating to next::method, checks $info for encode_column and, if present, installs a DBIO::FilterColumn filter_to_storage hook that hashes the value on write (skipping undef, references and literal SQL values) plus a check method (check_$column by default, or encode_check_method). An unknown encode_class, the module of the class missing, an unknown encode_args key or an algorithm outside the Digest list throws here, when the class is loaded. Called the same way as the plain register_column; not normally called directly.
COLUMN OPTIONS
encode_column-
Boolean; enables encoding for this column.
encode_check_method-
Method name to install for verification. Defaults to
check_${column}. encode_class-
Bcrypt(default),Argon2orDigest. Any other class is refused when the class is loaded. encode_args-
Hashref with optional settings. A key the class does not know is refused when the class is loaded.
cost(Bcrypt)-
Integer from 4 to 31, default
12. t_cost,m_cost,parallelism(Argon2)-
Time cost (positive integer, default
3), memory cost (a number with an optionalk,MorGsuffix, default32M) and parallelism (positive integer, default1). algorithm(Digest)-
SHA-256 (default), SHA-384, SHA-512, SHA3-224, SHA3-256, SHA3-384 or SHA3-512; another name is refused when the class is loaded.
salt_length(Digest)-
Salt size in bytes, a non-negative integer (default:
16,0disables salting). accept_unencoded(every class)-
Boolean, default
0. See "Verification".
RANDOMNESS
Salts come from Crypt::URandom, a required module. Where it cannot deliver the bytes, encoding throws no secure random source -- install Crypt::URandom.
SEE ALSO
AUTHOR
DBIO & DBIx::Class Authors
COPYRIGHT AND LICENSE
Copyright (C) 2026 DBIO Authors Portions Copyright (C) 2005-2025 DBIx::Class Authors Based on DBIx::Class, heavily modified.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.