Index: trunk/extensions/DonationInterface/payflowpro_gateway/payflowpro_gateway.body.php |
— | — | @@ -37,7 +37,8 @@ |
38 | 38 | */ |
39 | 39 | public function execute( $par ) { |
40 | 40 | global $wgRequest, $wgOut, $wgUser, $wgScriptPath, $wgPayFlowProGatewayCSSVersion; |
41 | | - |
| 41 | + session_cache_limiter( 'nocache' ); |
| 42 | + $this->fnPayflowEnsureSession(); |
42 | 43 | $this->setHeaders(); |
43 | 44 | |
44 | 45 | $wgOut->addHeadItem( 'validatescript', '<script type="text/javascript" language="javascript" src="' . |
— | — | @@ -67,43 +68,10 @@ |
68 | 69 | |
69 | 70 | |
70 | 71 | $wgOut->addScript( Skin::makeVariablesScript( $scriptVars ) ); |
| 72 | + // establish the edit token to prevent csrf |
| 73 | + global $wgPayflowGatewaySalt; |
| 74 | + $token = $this->fnPayflowEditToken( $wgPayflowGatewaySalt ); //$wgUser->editToken( 'mrxc877668DwQQ' ); |
71 | 75 | |
72 | | - // create token if one doesn't already exist |
73 | | - $token = $wgUser->editToken( 'mrxc877668DwQQ' ); |
74 | | - |
75 | | - // Declare form post variables |
76 | | - $data = array( |
77 | | - 'amount' => '', |
78 | | - 'email' => '', |
79 | | - 'fname' => '', |
80 | | - 'mname' => '', |
81 | | - 'lname' => '', |
82 | | - 'street' => '', |
83 | | - 'city' => '', |
84 | | - 'state' => '', |
85 | | - 'zip' => '', |
86 | | - 'country' => '', |
87 | | - 'card_num' => '', |
88 | | - 'expiration' => '', |
89 | | - 'cvv' => '', |
90 | | - 'currency' => '', |
91 | | - 'payment_method' => '', |
92 | | - 'order_id' => '', |
93 | | - 'numAttempt' => '', |
94 | | - 'referrer' => '', |
95 | | - 'utm_source' => '', |
96 | | - 'utm_medium' => '', |
97 | | - 'utm_campaign' => '', |
98 | | - 'language' => '', |
99 | | - 'comment' => '', |
100 | | - 'anonymous' => '', |
101 | | - 'optout' => '', |
102 | | - 'token' => $token, |
103 | | - 'contribution_tracking_id' => '', |
104 | | - 'data_hash' => '', |
105 | | - 'action' => '', |
106 | | - ); |
107 | | - |
108 | 76 | $error[] = ''; |
109 | 77 | |
110 | 78 | // find out if amount was a radio button or textbox, set amount |
— | — | @@ -120,6 +88,7 @@ |
121 | 89 | |
122 | 90 | // track the number of attempts the user has made |
123 | 91 | $numAttempt = ( $wgRequest->getText( 'numAttempt' ) == '' ) ? '0' : $wgRequest->getText( 'numAttempt' ); |
| 92 | + |
124 | 93 | // Populate from data |
125 | 94 | $data = array( |
126 | 95 | 'amount' => $amount, |
— | — | @@ -149,6 +118,7 @@ |
150 | 119 | 'anonymous' => $wgRequest->getText( 'comment-option' ), |
151 | 120 | 'optout' => $wgRequest->getText( 'email' ), |
152 | 121 | 'test_string' => $wgRequest->getText( 'process' ), //for showing payflow string during testing |
| 122 | + 'token' => $token, |
153 | 123 | 'contribution_tracking_id' => $wgRequest->getText( 'contribution_tracking_id' ), |
154 | 124 | 'data_hash' => $wgRequest->getText( 'data_hash' ), |
155 | 125 | 'action' => $wgRequest->getText( 'action' ), |
— | — | @@ -164,9 +134,9 @@ |
165 | 135 | |
166 | 136 | // Check form for errors and display |
167 | 137 | // match token |
168 | | - $success = $wgUser->matchEditToken( $token, 'mrxc877668DwQQ' ); |
169 | | - |
170 | | - if( $success ) { |
| 138 | + $token_check = ( $wgRequest->getText( 'token' ) ) ? $wgRequest->getText( 'token' ) : $token; |
| 139 | + $token_match = $this->fnPayflowMatchEditToken( $token_check, $wgPayflowGatewaySalt ); |
| 140 | + if( $token_match ) { |
171 | 141 | if( $data['payment_method'] == 'processed' ) { |
172 | 142 | //increase the count of attempts |
173 | 143 | ++$data['numAttempt']; |
— | — | @@ -197,6 +167,7 @@ |
198 | 168 | wfRunHooks( 'PayflowGatewayReject', array( &$this, &$data )); |
199 | 169 | |
200 | 170 | $this->fnPayflowDisplayDeclinedResults( '' ); |
| 171 | + $this->fnPayflowUnsetEditToken(); |
201 | 172 | } |
202 | 173 | |
203 | 174 | // if the transaction was flagged for processing |
— | — | @@ -204,6 +175,7 @@ |
205 | 176 | // expose a hook for external handling of trxns ready for processing |
206 | 177 | wfRunHooks( 'PayflowGatewayProcess', array( &$this, &$data )); |
207 | 178 | $this->fnPayflowProcessTransaction( $data, $payflow_data ); |
| 179 | + $this->fnPayflowUnsetEditToken(); |
208 | 180 | } |
209 | 181 | |
210 | 182 | // expose a hook for any post processing |
— | — | @@ -213,7 +185,11 @@ |
214 | 186 | //Display form for the first time |
215 | 187 | $this->fnPayflowDisplayForm($data, $error); |
216 | 188 | } |
217 | | - } |
| 189 | + } else { |
| 190 | + // there's a token mismatch |
| 191 | + $error['general']['token-mismatch'] = wfMsg( 'payflowpro_gateway-token-mismatch' ); |
| 192 | + $this->fnPayflowDisplayForm( $data, $error ); |
| 193 | + } |
218 | 194 | } |
219 | 195 | |
220 | 196 | /** |
— | — | @@ -320,7 +296,20 @@ |
321 | 297 | Xml::openElement( 'div', array( 'id' => 'mw-creditcard-intro' ) ) . |
322 | 298 | Xml::tags( 'p', array( 'class' => 'mw-creditcard-intro-msg' ), wfMsg( 'payflowpro_gateway-form-message' ) ) . |
323 | 299 | Xml::closeElement( 'div' ); |
324 | | - |
| 300 | + |
| 301 | + // provide a place at the top of the form for displaying general messages |
| 302 | + if ( $error['general'] ) { |
| 303 | + $form .= Xml::openElement( 'div', array( 'id' => 'mw-payflow-general-error' )); |
| 304 | + if ( is_array( $error['general'] )) { |
| 305 | + foreach ( $error['general'] as $error_msg ) { |
| 306 | + $form .= Xml::tags( 'p', array( 'class' => 'creditcard-error-msg' ), $error_msg ); |
| 307 | + } |
| 308 | + } else { |
| 309 | + $form .= Xml::tags( 'p', array( 'class' => 'creditcard-error-msg' ), $error_msg ); |
| 310 | + } |
| 311 | + $form .= Xml::closeElement( 'div' ); |
| 312 | + } |
| 313 | + |
325 | 314 | // open form |
326 | 315 | $form .= Xml::openElement( 'div', array( 'id' => 'mw-creditcard-form' ) ) . |
327 | 316 | Xml::element( 'p', array( 'class' => 'creditcard-error-msg' ), $error['retryMsg'] ) . |
— | — | @@ -962,5 +951,79 @@ |
963 | 952 | return $payflowCurrencies; |
964 | 953 | } |
965 | 954 | |
966 | | - |
| 955 | + /** |
| 956 | + * Establish an 'edit' token to help prevent CSRF, etc |
| 957 | + * |
| 958 | + * We use this in place of $wgUser->editToken() b/c currently |
| 959 | + * $wgUser->editToken() is broken (apparently by design) for |
| 960 | + * anonymous users. Using $wgUser->editToken() currently exposes |
| 961 | + * a security risk for non-authenticated users. Until this is |
| 962 | + * resolved in $wgUser, we'll use our own methods for token |
| 963 | + * handling. |
| 964 | + * |
| 965 | + * @var mixed $salt |
| 966 | + * @return string |
| 967 | + */ |
| 968 | + function fnPayflowEditToken( $salt='' ) { |
| 969 | + if ( !isset( $_SESSION[ 'payflowEditToken' ] )) { |
| 970 | + //generate unsalted token to place in the session |
| 971 | + $token = self::fnPayflowGenerateToken(); |
| 972 | + $_SESSION[ 'payflowEditToken' ] = $token; |
| 973 | + } else { |
| 974 | + $token = $_SESSION[ 'payflowEditToken' ]; |
| 975 | + } |
| 976 | + |
| 977 | + if ( is_array( $salt )) { |
| 978 | + $salt = implode( "|", $salt ); |
| 979 | + } |
| 980 | + return md5( $token . $salt ) . EDIT_TOKEN_SUFFIX; |
| 981 | + } |
| 982 | + |
| 983 | + /** |
| 984 | + * Generate a token string |
| 985 | + * |
| 986 | + * @var mixed $salt |
| 987 | + * @return string |
| 988 | + */ |
| 989 | + public static function fnPayflowGenerateToken( $salt='' ) { |
| 990 | + $token = dechex( mt_rand() ) . dechex( mt_rand() ); |
| 991 | + return md5( $token . $salt ); |
| 992 | + } |
| 993 | + |
| 994 | + /** |
| 995 | + * Determine the validity of a token |
| 996 | + * |
| 997 | + * @var string $val |
| 998 | + * @var mixed $salt |
| 999 | + * @return bool |
| 1000 | + */ |
| 1001 | + function fnPayflowMatchEditToken( $val, $salt='' ) { |
| 1002 | + // fetch a salted version of the session token |
| 1003 | + $sessionToken = $this->fnPayflowEditToken( $salt ); |
| 1004 | + if ( $val != $session_token ) { |
| 1005 | + wfDebug( "PayflowproGateway::fnPayflowMatchEditToken: broken session data\n" ); |
| 1006 | + } |
| 1007 | + return $val == $sessionToken; |
| 1008 | + } |
| 1009 | + |
| 1010 | + /** |
| 1011 | + * Unset the payflow edit token from a user's session |
| 1012 | + */ |
| 1013 | + function fnPayflowUnsetEditToken() { |
| 1014 | + unset( $_SESSION[ 'payflowEditToken' ] ); |
| 1015 | + } |
| 1016 | + |
| 1017 | + /** |
| 1018 | + * Ensure that we have a session set for the current user |
| 1019 | + * |
| 1020 | + * If we do not have a session set for the current user, |
| 1021 | + * start the session. |
| 1022 | + */ |
| 1023 | + public function fnPayflowEnsureSession() { |
| 1024 | + // if the session is already started, do nothing |
| 1025 | + if ( session_id() ) return; |
| 1026 | + |
| 1027 | + // otherwise, fire it up using global mw function wfSetupSession |
| 1028 | + wfSetupSession(); |
| 1029 | + } |
967 | 1030 | } // end class |
Index: trunk/extensions/DonationInterface/payflowpro_gateway/payflowpro_gateway.i18n.php |
— | — | @@ -102,6 +102,7 @@ |
103 | 103 | 'donate_interface-AUD' => 'AUD: Australian Dollar', |
104 | 104 | 'donate_interface-CAD' => 'CAD: Canadian Dollar', |
105 | 105 | 'donate_interface-JPY' => 'JPY: Japanese Yen', |
| 106 | + 'payflowpro_gateway-token-mismatch' => 'Your session has expired. Please try filling out and submitting the form again.', |
106 | 107 | ); |
107 | 108 | |
108 | 109 | /** Message documentation (Message documentation) |
Index: trunk/extensions/DonationInterface/payflowpro_gateway/payflowpro_gateway.php |
— | — | @@ -38,11 +38,20 @@ |
39 | 39 | $wgPayflowProUserID = ''; //if one or more users are set up, authorized user ID, else same as VENDOR |
40 | 40 | $wgPayflowProPassword = ''; //merchant login password |
41 | 41 | |
| 42 | +/** |
| 43 | + * A string or array of strings for making tokens more secure |
| 44 | + * |
| 45 | + * Please set this! If you do not, tokens are easy to get around, which can |
| 46 | + * potentially leave you and your users vulnerable to CSRF or other forms of |
| 47 | + * attack. |
| 48 | + */ |
| 49 | +$wgPayflowGatewaySalt = ''; |
| 50 | + |
42 | 51 | global $wgPayflowGatewayDBserver, $wgPayflowGatewayDBname, $wgPayflowGatewayDBuser, $wgPayflowGatewayDBpassword; |
43 | | -$wgPayflowGatewayDBserver = ( !$wgPayflowGatewayDBserver ) ? $wgDBserver : $wgPayflowGatewayDBserver; |
44 | | -$wgPayflowGatewayDBname = ( !$wgPayflowGatewayDBname ) ? $wgDBname : $wgPayflowGatewayDBname; |
45 | | -$wgPayflowGatewayDBuser = ( !$wgPayflowGatewayDBuser ) ? $wgDBuser : $wgPayflowGatewayDBuser; |
46 | | -$wgPayflowGatewayDBpassword = ( !$wgPayflowGatewayDBpassword ) ? $wgDBpassword : $wgPayflowGatewayDBpassword; |
| 52 | +$wgPayflowGatewayDBserver = $wgDBserver; |
| 53 | +$wgPayflowGatewayDBname = $wgDBname; |
| 54 | +$wgPayflowGatewayDBuser = $wgDBuser; |
| 55 | +$wgPayflowGatewayDBpassword = $wgDBpassword; |
47 | 56 | |
48 | 57 | function payflowGatewayConnection() { |
49 | 58 | global $wgPayflowGatewayDBserver, $wgPayflowGatewayDBname; |