Forum Discussion
Joel_66443
Nimbostratus
Jun 16, 2009Encrypting POST variables before they hit the server
Hi All,
I'm attempting to encrypt two form variables as they are in transit from the user's browser to the web server. That is, I want to encrypt the two variables before they hit the web server.
The variables in this case consist of a credit card number (CCNumber) and ccv number (CCV) number.
In order to accomplish this, I am attempting to split the HTTP: Payload into it's key/value pairs, then encrypt the two form values, then finally compile a new HTTP: Payload to replace the old before sending it onto the webserver.
I'm finding that it's not quite as simple to implement as first thought. whilst the iRule does indeed encrypt the two variables and rebuild a new HTTP: Payload, it fails to replace the old with the new, balking with the error:
- Out of bounds (line 1) invoked from within "HTTP::payload replace 0 [string length $::newPayload] $::newPayload"
Attempts to place the 'HTTP::payload replace' into HTTP_RESPONSE_DATA fails to modify the POST data in any way. It's still unencrypted to the server.
I'd sincerely appreciate if a more experienced engineer could have a glance over the code and pick out any key errors.
- Jason
when RULE_INIT {
set ::aes_key 7ef82a9b038c19d4a6b2c014d812
}
when HTTP_REQUEST {
Prevent the server from sending a compressed response
remove the compression offerings from the client
HTTP::header remove "Accept-Encoding"
Don't allow response data to be chunked
if { [HTTP::version] eq "1.1" } {
Force downgrade to HTTP 1.0, but still allow keep-alive connections.
Since HTTP 1.1 is keep-alive by default, and 1.0 isn't,
we need make sure the headers reflect the keep-alive status.
Check if this is a keep alive connection
if { [HTTP::header is_keepalive] } {
Replace the connection header value with "Keep-Alive"
HTTP::header replace "Connection" "Keep-Alive"
}
Set server side request version to 1.0
This forces the server to respond without chunking
HTTP::version "1.0"
log local0. "Original Payload: [HTTP::payload]"
log local0. "Original Payload Length: [string length [HTTP::payload]]"
set parametersList [split [HTTP::payload] "&"]
for {set i 0} {$i < [llength $parametersList]} {incr i} {
set parameter [split [lindex $parametersList $i] "="]
set ::postVars([lindex $parameter 0]) [lindex $parameter 1];
}
encrypt the credit card number and ccv
set card_number_encrypted [URI::encode [b64encode [AES::encrypt $::aes_key $::postVars(CCNumber)]]]
set ccv_number_encrypted [URI::encode [b64encode [AES::encrypt $::aes_key $::postVars(CCV)]]]
create a new http payload
set ::newPayload "CCNumber=$card_number_encrypted&CCV=$ccv_number_encrypted"
log local0. "New Payload: $::newPayload"
log local0. "New Payload Length: [string length $::newPayload]"
replace the old payload with the new
HTTP::payload replace 0 [string length $::newPayload] $::newPayload
}
}
when HTTP_RESPONSE {
}
when HTTP_RESPONSE_DATA {
HTTP::payload replace 0 [string length $::newPayload] $::newPayload
}
The following logs demonstrate that the script is successfully compiling a new payload, but is failing to apply it to the stream.
Original Payload: CCNumber=1111+1111+1111+1111&CCV=123
Original Payload Length: 53
New Payload: CCNumber=edCEWkxt0jh28CkaNezCLift0eHxpJRGiNLMXoBk8RU5AnBMo9xzaubcwspj%2bLLqZXJfDsaUl9ij&CCV=OaszTOZA%2by5P2UAaNezCLift0eHxpHggtCfx8%2fSs4q9uNtTGxEthMTA%3d
New Payload Length: 171
- hoolio
Cirrostratus
Hi, - Joel_66443
Nimbostratus
True, in most situations it would be prudent to encrypt the entire connection rather than a few elements of the message sent from the client. My case is different however as I am designing a system for PCI compliance which will encrypt credit card values as they're entering the system, and then decrypt them as they're leaving the system. - hoolio
Cirrostratus
That's an interesting concept.when RULE_INIT { Hardcode an encryption key set ::aes_key 7ef82a9b038c19d4a6b2c014d812 Credit Card parameter name set ::cc_param_name "CCNumber" Card Code Verification parameter name set ::ccv_param_name "CCV" Maximum collection size (in bytes) set ::max_collect_size 1000000 Log debug messages? 1=yes, 0=no. set ::cc_encrypt_debug 1 } when HTTP_REQUEST { Check if request is a POST if {[HTTP::method] eq "POST"}{ If we're logging debug messages, save the client IP:port to make log lines shorter. if {$::cc_encrypt_debug}{ set log_prefix "[IP::client_addr]:[TCP::client_port]" log local0. "$log_prefix: New POST request to [HTTP::host][HTTP::uri]" } Trigger collection for up to 1MB of data if {([HTTP::header exists "Content-Length"]) && ([HTTP::header "Content-Length"] <= $::max_collect_size)}{ set content_length [HTTP::header "Content-Length"] } else { set content_length $::max_collect_size } if { [info exists content_length] && $content_length > 0} { HTTP::collect $content_length if {$::cc_encrypt_debug}{log local0. "$log_prefix: Collecting $content_length bytes"} } } } when HTTP_REQUEST_DATA { if {$::cc_encrypt_debug}{log local0. "$log_prefix: Original payload ([HTTP::payload length] bytes): [HTTP::payload]"} Parse the Credit Card number and CCV parameter values from the payload using a URI::query set cc_unencrypted [URI::query "?[HTTP::payload]" $::cc_param_name] set ccv_unencrypted [URI::query "?[HTTP::payload]" $::ccv_param_name] if {$::cc_encrypt_debug}{log local0. "$log_prefix: Parsed \$cc_unencrypted=$cc_unencrypted, \$ccv_unencrypted=$ccv_unencrypted"} Replace the CC with the encrypted version if a CC parameter value was parsed if {[string length $cc_unencrypted]}{ Replace the Credit Card number parameter name=value pair with the encrypted and encoded version. We use the full param_nam=value for searching/replacing to reduce the chance of a false match HTTP::payload replace 0 [HTTP::payload length] [regsub -all "${::cc_param_name}=${cc_unencrypted}" [HTTP::payload] "${::cc_param_name}=[URI::encode [b64encode [AES::encrypt $::aes_key $cc_unencrypted]]]"] if {$::cc_encrypt_debug}{log local0. "$log_prefix: CC updated payload ([HTTP::payload length] bytes): [HTTP::payload]"} } Replace the CCV with the encrypted version if a CCV parameter value was parsed if {[string length $ccv_unencrypted]}{ Replace the Card Code Verification parameter name=value pair with the encrypted and encoded version. We use the full param_nam=value for searching/replacing to reduce the chance of a false match HTTP::payload replace 0 [HTTP::payload length] [regsub -all "${::ccv_param_name}=${ccv_unencrypted}" [HTTP::payload] "${::ccv_param_name}=[URI::encode [b64encode [AES::encrypt $::aes_key $ccv_unencrypted]]]"] if {$::cc_encrypt_debug}{log local0. "$log_prefix: CCV updated payload ([HTTP::payload length] bytes): [HTTP::payload]"} } }
- hoolio
Cirrostratus
Here is some sample log output from a quick test: - Joel_66443
Nimbostratus
Many thanks Aaron. The script works perfectly! - hoolio
Cirrostratus
The only issue with potentially overwriting the CC values was if you were using a global variable to store the CC numbers. The example I posted uses local variables which are specific to one TCP connection, so it wouldn't be an issue.
Recent Discussions
Related Content
DevCentral Quicklinks
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com
Discover DevCentral Connects