Perl实现Shopify Multipass Token生成时持续返回无效请求
Shopify Multipass Token生成问题排查(Perl实现)
问题背景
使用Perl生成Shopify Multipass Token时持续收到「无效请求」响应,已确认以下内容无误:
- 客户数据格式与内容:
{"email": "randomEmail@gmail.com", "last_name": "last_name", "first_name": "first_name"} - 店铺API密钥、店铺URL
- 加密密钥与签名密钥的生成逻辑(基于API密钥SHA256哈希拆分)
- UTC时间格式正确性
通过Python的spylib测试可生成有效Token,注释时间验证逻辑后,Perl生成的Token长度与有效Token一致,怀疑问题出在加密环节。
当前实现代码
sub generate_multipass_token { use JSON qw(encode_json); use MIME::Base64 qw(encode_base64); use Digest::SHA qw(sha256 hmac_sha256); use Crypt::CBC; use Crypt::Cipher::AES; use POSIX qw(strftime); use Encode qw(encode); my ($customer_data) = @_; my $store_api_key = ''; my $store_url = ''; my $hash = sha256($store_api_key); my $encryption_key = substr($hash, 0, 16); my $signing_key = substr($hash, 16, 16); my ($sec, $min, $hour, $day, $mon, $year) = gmtime(); my $formatted_time = sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ", $year+1900, $mon+1, $day, $hour, $min, $sec); # Add the formatted time to the customer data $customer_data->{created_at} = $formatted_time; # JSON-encode the customer data my $json_customer_data = encode_json($customer_data); # Encrypt the customer data using AES my $iv = Crypt::CBC->random_bytes(16); my $cipher = Crypt::CBC->new( -key => $encryption_key, -iv => $iv, -cipher => 'Cipher::AES', -header => 'none', ); my $encrypted_data = $cipher->encrypt($json_customer_data); $encrypted_data = $iv . $encrypted_data; my $signature = hmac_sha256($encrypted_data, $signing_key); my $token = encode_base64url($encrypted_data . $signature); return "$store_url/account/login/multipass/" . $token; } sub encode_base64url { my $data = shift; my $encoded = encode_base64($data, ''); $encoded =~ tr{+/}{-_}; $encoded =~ s/=+$//; return $encoded; }
加密环节排查方向与修复建议
1. 显式指定AES填充与密钥参数
Shopify要求使用AES-128-CBC模式 + PKCS#7填充,原代码未显式指定填充规则,且Cipher模块名称有误,需调整加密初始化逻辑:
my $cipher = Crypt::CBC->new( -key => $encryption_key, -iv => $iv, -cipher => 'Crypt::Cipher::AES', # 修正模块名称 -header => 'none', -padding => 'pkcs7', # 显式指定PKCS7填充 -literal_key => 1, # 直接使用原始密钥,不做额外哈希处理 -keysize => 16, # 明确AES-128密钥长度 );
2. 确保JSON编码为UTF-8格式
encode_json默认输出UTF-8,但显式编码可避免潜在字符集问题:
my $json_customer_data = encode('UTF-8', encode_json($customer_data));
3. 简化UTC时间生成逻辑
用strftime直接生成标准UTC时间,避免手动拼接可能的格式错误:
my $formatted_time = strftime("%Y-%m-%dT%H:%M:%SZ", gmtime());
修改后的完整代码
sub generate_multipass_token { use JSON qw(encode_json); use MIME::Base64 qw(encode_base64); use Digest::SHA qw(sha256 hmac_sha256); use Crypt::CBC; use Crypt::Cipher::AES; use POSIX qw(strftime); use Encode qw(encode); my ($customer_data) = @_; my $store_api_key = ''; # 填入你的店铺API密钥 my $store_url = ''; # 填入你的店铺URL my $hash = sha256($store_api_key); my $encryption_key = substr($hash, 0, 16); my $signing_key = substr($hash, 16, 16); # 生成标准UTC时间 my $formatted_time = strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()); # 添加时间参数到客户数据 $customer_data->{created_at} = $formatted_time; # JSON编码并确保UTF-8格式 my $json_customer_data = encode('UTF-8', encode_json($customer_data)); # 初始化符合Shopify要求的AES-CBC加密器 my $iv = Crypt::CBC->random_bytes(16); my $cipher = Crypt::CBC->new( -key => $encryption_key, -iv => $iv, -cipher => 'Crypt::Cipher::AES', -header => 'none', -padding => 'pkcs7', -literal_key => 1, -keysize => 16, ); my $encrypted_data = $cipher->encrypt($json_customer_data); $encrypted_data = $iv . $encrypted_data; # 生成HMAC签名并拼接Token my $signature = hmac_sha256($encrypted_data, $signing_key); my $token = encode_base64url($encrypted_data . $signature); return "$store_url/account/login/multipass/" . $token; } sub encode_base64url { my $data = shift; my $encoded = encode_base64($data, ''); $encoded =~ tr{+/}{-_}; $encoded =~ s/=+$//; return $encoded; }
内容的提问来源于stack exchange,提问作者adriano515
相关产品推荐
相关产品推荐

