I thank all of you for this exhaustive treatment. Sometimes I regret
that what flies around in forum threads never makes it into the docs.
On 02/17/2017 02:32 PM, Andy LoPresto wrote:
Russ,
Both Bryan and Joe are exactly correct in their responses. To add some
context, each sensitive property value is encrypted using the
specified algorithm in nifi.properties. By default, and therefore on
any unmodified system, this is PBEWITHMD5AND256BITAES-CBC-OPENSSL,
which means that the actual cipher applied is AES with CBC mode of
operation [1] using a 256-bit key which is derived from the provided
“nifi.sensitive.props.key” value (which is really a password, not a
true key) using the OpenSSL EVP_BytesToKey [2] key derivation function
(KDF) — a modified version of PKCS #5 v1.5’s MD5-based PBKDF1 [3]
which is identical if the combined length of the key and IV is less
than or equal to an MD5 digest and non-standard if greater.
A random 16 byte salt is generated during the encrypt process (the
salt length is actually set to be the block length of the cipher in
question — AES is always 16 bytes, but if you choose to use DES for
example, this value would be 8 bytes), and this salt is provided,
along with an iteration count (1000 by default) to the KDF. Internally
to the application, this process occurs within the Jasypt library
(slated for removal in NIFI-3116 [4]) but you can see a compatible
Java native implementation of this process in the ConfigEncryptionTool
I wrote [5].
If the “nifi.sensitive.props.key” value is empty, a hard-coded
password is used as Bryan mentioned. This is why ALL DOCUMENTATION
should recommend setting that value to a unique and strong passphrase
prior to deployment [6].
Once the key is derived, the sensitive value is encrypted and the salt
and cipher text are concatenated and then encoded in hexadecimal. This
output is wrapped in “enc{“ and “}” tokens to denote the value is
encrypted, and then stored in the flow.xml.gz. As Joe and Bryan
pointed out, the sensitive value (in plaintext or encrypted form) is
never sent over the REST API to any client, including the UI. When
editing a processor property that is sensitive, the UI displays a
static placeholder.
If two values are the same (i.e. the same password in an
EncryptContent processor encrypting and an EncryptContent processor
decrypting later in the same flow), the two encrypted values will be
completely different in the flow.xml.gz even though they were
encrypted with the same key. This is because of the random salt value
mentioned above.
The encrypt-config.sh script [7] in nifi-toolkit exposes the
functionality to “migrate” the flow.xml.gz encrypted values to be
encrypted with a new key. For example, if you read the above and feel
uncomfortable with your values encrypted with the default hard-coded
password rather than a unique passphrase, you can run this tool and
provide a new passphrase, and it will update nifi.properties with the
new passphrase (and encrypt it if you have encrypted configuration [8]
enabled) and decrypt all values in the flow.xml.gz and re-encrypt them
with the new key and re-populate them.
A small note on the migration tool — you may notice that after running
it, identical values will have identical encrypted values. This design
decision was made because the performance tradeoff of not
re-performing the KDF with a unique salt and cipher initialization on
each value is immense, and the threat model is weak because the key is
already present on the same system. This decision was further impacted
by the process by which Jasypt derives the keys. After NIFI-3116 is
completed, I feel confident we can improve the security of the cipher
texts by using unique IVs without incurring the substantial penalty
currently levied by the library structure.
Throughout this explanation, you may have had concerns about some of
the decisions made (algorithms selected, default values, etc.). There
are ongoing efforts to improve these points, as security is an
evolving process and attacks and computational processing availability
to attackers is always increasing. More of this is available in
relevant Jiras [9][10][11] and the Security Features Roadmap on the
wiki [12], but in general I am looking to harden the system by
restricting the algorithms used by default and available in general
and increasing the cost of all key derivations (both in time and
memory hardness via PBKDF2, bcrypt, and scrypt [13]). Obviously the
obstacles in this effort are backwards compatibility and legacy
support. It is a balance, but with the support of the community, I see
us continuing to move forward with regards to security while making
the user experience even easier.
I hope that provided some helpful information in response to your
question.
[1]
https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
[2] https://www.openssl.org/docs/man1.1.0/crypto/EVP_BytesToKey.html
[3] https://tools.ietf.org/html/rfc2898#section-5.1
[4] https://issues.apache.org/jira/browse/NIFI-3116
[5]
https://github.com/apache/nifi/blob/master/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy#L555
[6]
https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#how-to-install-and-start-nifi
[7]
https://github.com/apache/nifi/blob/master/nifi-toolkit/nifi-toolkit-assembly/src/main/resources/bin/encrypt-config.sh
[8]
https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#encrypted-passwords-in-configuration-files
[9] https://issues.apache.org/jira/browse/NIFI-2656
[10] https://issues.apache.org/jira/browse/NIFI-2653
[11] https://issues.apache.org/jira/browse/NIFI-1465
<https://issues.apache.org/jira/browse/NIFI-1480>
[12]
https://cwiki.apache.org/confluence/display/NIFI/Security+Feature+Roadmap
[13] https://github.com/apache/nifi/pull/201
Andy LoPresto
[email protected] <mailto:[email protected]>
/[email protected] <mailto:[email protected]>/
PGP Fingerprint: 70EC B3E5 98A6 5A3F D3C4 BACE 3C6E F65B 2F7D EF69
On Feb 17, 2017, at 11:40 AM, Bryan Bende <[email protected]
<mailto:[email protected]>> wrote:
Russell,
Sensitive property values are stored in the flow.xml.gz in encrypted
form. The encryption is based on the value of
nifi.sensitive.props.key= and if you haven't set one there is a
default in the code. If you have the same sensitive properties key in
both instances than you can copy the flow, or if you didn't set one in
both instances which means you have the same. If you had different
keys then one instance wouldn't be able to decrypt the values from the
other instance.
Thanks,
Bryan
On Fri, Feb 17, 2017 at 2:35 PM, Russell Bateman
<[email protected] <mailto:[email protected]>> wrote:
1. Where are the values of sensitive-value properties stored?
2. How do they work? (if not obvious from #1)
3. Can /flow.xml.g//z/ be copied wholesale or in part to another
instance of NiFi?
Thanks,
Russ