The company I work for is looking for a payment processor. Ideally we want a fully and seamlessly integrated solution. I came across this from one of the recommended companies I had to look in to

This is from a sample page. I don't have a sandbox account yet to actually try anything out (but I requested one).

Here's the immediate piece of code I have an issue with:

<input type="button" onclick="TAMPER_PROOF_SEAL.value = hex_md5(TPS.SECRET_KEY.value + ACCOUNT_ID.value)" value="Generate API Signature">

The rest here is most of the HTML from the 'sample page' sands header and body tags.

<table border=1>
 <TR><TD colspan=2><CENTER><B>company.js API Signature Generator</B></CENTER></TD></TR>
<form name="TPS">
    <TD><input type="text" value="" name="SECRET_KEY" align="top" size="50"> <font size="-2" color="RED">API Sig1</font></TD>
<form name="main" method="post">
        <input type="text" value="" name="ACCOUNT_ID" align="top" size="50"> <font size="-2" color="RED">API Sig2</font><br>
        12-digit Company 2.0 Account ID

    <TD>API Signature:</TD>
        <input type="text" value="" name="TAMPER_PROOF_SEAL" align="top" size="50"><br>
        <input type="button" onclick="TAMPER_PROOF_SEAL.value = hex_md5(TPS.SECRET_KEY.value + ACCOUNT_ID.value)" value="Generate API Signature"><br>
        MD5 hash of Secret Key and Account ID


It's a 'custom' MD5 hashing library of theirs.

This strikes me as being weak as hell security.

  • A. MD5 hashses are terrible
  • B. It's trivial to ignore the hash code anyway, and our "secret" is out. I'm reading through more documentation to make sure it's not as dumb as it looks.

Is Direct Posting still a sound idea? Authorize.NET seemed to have a great one with Accept.JS. Unfortunately their costs will kill us.

Or this piece, showing more end to end functionality. I don't see the security:

<!DOCTYPE html>
    <title>Company Encryption &amp; Tokenization</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

Place data-encrypted-name="INPUT_NAME" attribute on all form controls to be encrypted. See below for acceptable values.
Do not include name="" attribute on form controls with data-encrypted-name="":

    Card Number

    Security Code

    2 digit month

    2 digit year

    4 digit date: MMYY

    Checking or Savings: C/S

    Account Number

    Routing Number

<!-- Set "action" to URL of script on merchant's web server that will receive token. -->
<form id="payment-form" method="post" class="form" action="***URL of script on merchant's server goes here***">
    <input type="hidden" name="MODE" value="LIVE">

        <legend>Your Info</legend>
            <label for="NAME1">Name</label><br>
            <input type="text" id="NAME1" name="NAME1" value="John"><br>
            <input type="text" id="NAME2" name="NAME2" value="Doe">

        <legend>Credit Info</legend>
            <label for="AMOUNT">Amount</label><br>
            <input type="text" id="AMOUNT" name="AMOUNT" value="5.00">              
            <label for="CARD_ACCOUNT">Credit Card Number</label><br>
            <input type="text" id="CARD_ACCOUNT" data-encrypted-name="CARD_ACCOUNT" value="4111111111111111">
            <label for="CARD_CVV2">CVV Code</label><br>
            <input type="text" id="CARD_CVV2" data-encrypted-name="CARD_CVV2" value="123">
            <label for="EXPMO">Month</label>
            <select id="EXPMO" data-encrypted-name="EXPMO">
                <option value="01">01</option>
                <option value="02">02</option>
                <option value="03">03</option>
                <option value="04">04</option>
                <option value="05">05</option>
                <option value="06">06</option>
                <option value="07">07</option>
                <option value="08">08</option>
                <option value="09">09</option>
                <option value="10">10</option>
                <option value="11">11</option>
                <option value="12">12</option>

            <label for="EXPYR">Year</label>
            <select data-encrypted-name="EXPYR" id="EXPYR">                                                 
                <option value="16">2016</option>
                <option value="17">2017</option>
                <option value="18">2018</option>
                <option value="19">2019</option>
                <option value="20">2020</option>
                <option value="21">2021</option>
                <option value="22">2022</option>
                <option value="23">2023</option>
                <option value="24">2024</option>
                <option value="25">2025</option>
                <option value="26">2026</option>

    <p><button type="submit">Submit</button></p>
    <div class="form-response"></div>

<!-- [if using jquery: not required to use Company API] -->
<script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<!-- [@end if using jquery] -->

<!-- [production] -->
<script type="text/javascript" src="https://secure.fake.com/v3/company.js"></script>
<!-- [@end production] -->

<script type="text/javascript">

    jQuery(function($) {

        /* Company Set Credentials for Encryption & Tokenization
         * Identifies Account ID & API Signature
         * @param {string} accountID (required) - Account ID
         * @param {string} apiSignature (required) - API Signature
         * Company.setCredentials(accountID, apiSignature);
         * ========================================
        Company.setCredentials("***Gateway account ID goes here***", "***API Signature goes here***");

        /* Company Callback 
         * Function is called after Company.createToken completes
         * @param {object} response - json response from api call           
         * @param {string} error - if unable to connect to api 
         * ========================================
        var callback = function(response, error) {
            // cache form id
            var $form = $('#payment-form');
            // show response
            if (error) {
                // if unable to connect to api
            } else {
                if (response.STATUS !== '1') {
                    // Show form errors from api response
                } else {
                    // success message
                    // re-submit form to server
            // re-enable submit button
            $form.find('button').prop('disabled', false);

        // Event Listener Creates Token on submit
        $('#payment-form').on('submit',function(e) {

            // Prevent the form from submitting with the default action 
            // also prevent form submit when js error is thrown

            // cache form id
            var $form = $(this);

            // Disable the submit button to prevent repeated clicks
            $form.find('button').prop('disabled', true);

           /* Company Create Token  
            * Encrypts sensitive data, creates Precalculated Tamper Proof Seal and Token
            * @param {object/string} data (required)- form id (to get data from) of plain js 'payment-form' or jquery $('#payment-form') or...
            * js object {CARD_ACCOUNT: '4111111111111111', CARD_CVV2: '123', CARD_EXPIRE: '0824'}
            * @param {function} callback (required) - Name of function to be called after encryption & tokenization complete  
            * @param {object} debug @property {boolean} (optional) - used with form id...
            * Shows 2 input fields to displays values before and after encryption when form id is passed. 
            * Also shows console warnings if data-encrypted-name is not a valid api name                           
            * Company.createToken(data, callback):
            * ======================================== */
            Company.createToken($form, callback);
            // Company.createToken('payment-form', callback, {debug: true});
            // Company.createToken({CARD_ACCOUNT: '4111111111111111', CARD_CVV2: '123', CARD_EXPIRE: '0828', NAME1: 'John', NAME2: 'Doe'}, callback);
            // Company.createToken({PAYMENT_TYPE: 'ACH', ACH_ACCOUNT:'123456789', ACH_ROUTING: '123123123', NAME1: 'Jane', NAME2: 'Doe'}, callback);


  • 1
    1) Are you supposed to tell us that it's BluePay? I hope the free trial isn't under NDA... 2) So the user types the secret key, and the button's OnClick hashes that together with the AccountID to make a "tamper-proof-seal; 2a) how does the user get the secret key? 2b) what is the tamper proof seal used for? I agree this is a red flag, but it's not necessarily terrible depending on how this fits into the bigger picture. – Mike Ounsworth Aug 14 '18 at 12:21
  • 1
    Well, this is subjective. Use of API key and an associated secret key is quite common, it is the admin job to secure the connection between the communication. – mootmoot Aug 14 '18 at 12:41
  • 1
    Perhaps the correct question should be : is the API token and secret key is guessable and easily hijacked. – mootmoot Aug 14 '18 at 12:51
  • 1
    @kevingreen I hear you. It's about security approaches, which is better here than there, but it is too "read this code and tell me what you think" more than "this bit right here, does this meet the goal of X considering Y and Z?" – schroeder Aug 14 '18 at 13:51
  • 1
    @kevingreen feel free to ping me here if you post some new questions – Conor Mancone Aug 14 '18 at 14:24