Safety Guide
This document compiles some common security matters in the development and operation of minigames, to help developers understand, discover, and fix related security and vulnerability issues, avoiding losses to normal business operations. To make it easier to put into practice, some matters already have corresponding capabilities that support solving them.
Security Principles
During minigame development, it is easy to inadvertently introduce security issues. Following a good set of secure development principles can avoid most risks. Some principles are compiled below:
Code protection principle: Properly protect the source code. Before the first launch, the code submitted for review must be hardened to raise the cost of malicious reading, analysis, and countermeasures.
Least privilege principle: When submitting code for review, keep only regular player permissions and remove all special permissions, such as: permissions to produce tokens/virtual items, Mock code from the testing period, special interfaces, etc., to prevent malicious exploitation after launch.
Ticket authentication principle: After customizing the login state, hide the ticket properly. The ticket design should combine identity information, validity/timeliness information, package data information, permission information, etc., to avoid various privilege-escalation risks.
Mutual distrust principle: Every data exchange between front end and back end requires authentication. The back end must not trust data submitted by the front end. Important data must be validated for legitimacy based on the game's characteristics, context, and reasonableness, rejecting unexpected requests.
Campaign risk control principle: In campaign operations, distribution of cash and high-value items must have sufficiently reasonable thresholds, such as: time value, level value, account value, etc., to avoid being maliciously farmed by non-target players.
UGC untrusted principle: User-generated UGC content (images, text, audio, video, etc.) is untrusted. Before it is trusted and used, it must pass content safety review; only content verified as normal may be displayed publicly.
Code Hardening
Code hardening is a form of protection for the plaintext game code. It includes code obfuscation, code watermarking, code lock, and some anti-cheat capabilities, providing fairly comprehensive protection for the code and game operation. To make it easy to apply, the relevant capabilities are packaged together in the developer tool plugin — "Game Deep Protection". The capability items and the hardening level can be freely chosen according to the game's own situation.
Code Obfuscation
Obfuscation, built on code feature engineering, is a transformation that makes the code functionally equivalent while structurally heterogeneous and with recombined names. The transformed code does not change the original game logic, but the names, structure, and control flow change dramatically. Readability is almost entirely lost, greatly increasing the attacker's cost of understanding. Depending on the complexity of the transformation, the plugin provides three options — basic, intermediate, and advanced — and developers can weigh the trade-off between size inflation cost and the complexity of the resulting code.
Below are a few lines of simple example code, and the resulting code after choosing different obfuscation levels.
Example code:
{
let info = {name: "xiaoming", year: 2024, version: 2};
console.log(info);
info.version =3;
let more = {obj: info};
console.log(more.info);
more.obi.name ='sud';
console.log(more);
}Obfuscation result for the "basic" option:
let a=Math.log,b=Math.pow,c=Math.floor,d=Math.random,e=Math.exp,f=Math.abs,g=Math.round,h=Math.E,i=Math.LN10,j=Math.LN2,k=Math.LOG10E,l=Math.PI,m=Math.SQRT1_2,n=Math.SQRT2;{let a={name:"xiaoming",year:2024,version:2};console.log(a);a.version=3;let b={obj:a};console.log(b.obj);b.obj.name="sud";console.log(b)}Obfuscation result for the "intermediate" option:
let a=Math.log,b=Math.pow,c=Math.floor,d=Math.random,e=Math.exp,f=Math.abs,g=Math.round,h=Math.E,i=Math.LN10,j=Math.LN2,k=Math.LOG10E,l=Math.PI,m=Math.SQRT1_2,n=Math.SQRT2;let o=80,p=26,q=46,r=35,s=22,t=99,u=23,v=45,w=3,x=68,y=50,z=40,A=90,B=79,C=21,D=57,E=51,F=22,G=63,H=49,I=85,J=39,K=99,L=10,M=81,N=73,O=32,P=27,Q=94,R=1,S=41,T=60,U=74,V=11,W=88,X=64,Y=13,Z=37,_0=69,_1=11,_2=28,_3=11,_4=33,_5=87,_6=73,_7=50,_8=27,_9=79,ba=42,bb=35,bc=70,bd=50,be=31,bf=59,bg=93,bh=70,bi=43,bj=70,bk=16,bl=54,bm=57,bn=21,bo=77,bp=55,bq=2,br=28,bs=23,bt=88,bu=93,bv=58,bw=65,bx=72,by=68,bz=53,bA=32,bB=60,bC=16,bD=47,bE=71,bF=69;let bG=g(2*a(f(31*Y+70*bj)))<=g(a(13*13+70*bj)+a(be*be+70*bj)),bH=g(2*a(f(68*bo+bC*bC)))<=g(a(bC*bC+77*77)+a(16*bC+by*by)),bI=16*bC+99*t+94*Q+22*22<16*22+22*Q+99*16+Q*t,bJ=c((16+X+_1)/3)>=c(e((a(X)+a(11)+a(16))/3)),bK=g(2*a(f(57*_6+93*99)))<=g(a(bu*bu+73*_6)+a(99*K+57*bm)),bL=g(2*a(f(16*y+72*D)))>g(a(50*y+D*D)+a(16*bC+bx*bx)),bM=_4*_4+93*bu+47*47+79*_9<93*_4+79*bu+47*_9+33*bD,bN=c((O+G+_5)/3)<c(e((a(32)+a(_5)+a(G))/3)),bO=c((85+11+P)/3)<c(e((a(I)+a(V)+a(27))/3)),bP=c((x+37+23)/3)<c(e((a(bs)+a(x)+a(37))/3)),bQ=c((s+U+69)/3)<c(e((a(_0)+a(U)+a(22))/3)),bR=g(2*a(f(bx*bq+81*M)))<=g(a(72*bx+M*M)+a(M*M+2*bq)),bS=g(2*a(f(10*L+3*Z)))<=g(a(10*L+3*3)+a(10*L+37*37)),bT=28*_2+80*o+37*37<28*Z+80*Z+28*80,bU=g(2*a(f(32*_6+J*B)))<=g(a(32*32+39*J)+a(B*B+73*73)),bV=c((p+53+27)/3)<c(e((a(P)+a(bz)+a(p))/3)),bW=g(2*a(f(69*69+1*88)))<=g(a(1*R+_0*_0)+a(88*bt+69*69)),bX=g(2*a(f(50*be+V*_4)))>g(a(11*11+50*y)+a(33*_4+be*be)),bY=g(2*a(f(K*be+ba*ba)))>g(a(42*42+K*K)+a(42*ba+31*be)),bZ=10*L+M*M+74*U+Z*Z<10*74+U*Z+81*L+81*Z,b0=13*13+16*bk+31*31+B*B>=79*31+16*Y+79*Y+be*bk,b1=g(2*a(f(37*q+s*s)))<=g(a(22*s+37*Z)+a(s*s+46*q)),b2=58*58+22*s+bB*bB+_1*_1<58*_1+bB*s+60*bv+11*s,b3=g(2*a(f(69*37+54*bc)))>g(a(69*_0+54*bl)+a(37*Z+70*bc)),b4=g(2*a(f(D*D+70*br)))>g(a(57*D+br*br)+a(bc*bc+57*D)),b5=g(2*a(f(60*br+94*bo)))<=g(a(60*60+77*bo)+a(br*br+94*94)),b6=bl*bl+63*G+69*bF+88*bt>=54*69+88*63+69*88+63*bl,b7=58*58+63*G+70*bc>=bc*G+bv*bc+63*58,b8=R*R+40*40+54*bl+88*bt>=1*54+z*bt+40*bl+1*bt,b9=70*bh+81*M+Q*Q<70*M+81*Q+70*94,ca=37*Z+70*bh+73*N<Z*bh+70*N+73*37,cb=c((28+t+bf+by)/4)<c(e((a(68)+a(28)+a(99)+a(59))/4)),cc=99*K+57*bm+28*_2+40*40>=40*K+bm*z+99*28+57*_2,cd=c((bs+s+Z)/3)<c(e((a(22)+a(Z)+a(bs))/3)),ce=11*_1+57*D+_0*_0+60*60<bB*_0+11*bB+57*_0+57*_1,cf=81*81+bd*bd+W*W+16*16>=W*M+50*16+16*88+81*bd,cg=g(2*a(f(31*63+_9*w)))<=g(a(31*be+79*79)+a(w*w+63*63)),ch=c((35+H+T)/3)>=c(e((a(H)+a(bb)+a(60))/3)),ci=93*93+28*28+41*S+bA*bA>=_2*bA+93*_2+41*93+32*S,cj=_6**6+31*be+39*J>=73*be+73*39+39*be;{let q,a,s,b,u,c,d,e,z,f;let g=1;String.prototype.m=String.prototype.substr;String.prototype.s=function(a){let b=[];for(let c=0;c<this.length;c+=a)b.push(this.slice(c,c+a));return b};let h={m:function a(a,b){let c=a.length;let d=[];for(let e=0;e<c;++e){let f=-1;if(2===b){f=1*(e-56)%2;if(f<0)f+=c}if(3===b){f=1*(e-67)%6;if(f<0)f+=c}if(4===b){f=1*(e-47)%8;if(f<0)f+=c}if(7===b){f=1*(e-39)%7;if(f<0)f+=c}if(8===b){f=5*(e-27)%8;if(f<0)f+=c}if(f>=0)d[f]=a[e]}return d.join("")}};let i=h.m("wx",2);let j=i.s(2);let k=h.m("jlogob",3);let l=k.s(3);let m=h.m("ameyearn",4);let n=m.s(4);let o=h.m("sionver",7);let p=o.s(7);let r=h.m("inoxiagm",8);let t=r.s(8);let v="2,Wq,A,B";function w(a){let b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+./:;<=>?@[]^*`{|}~"',c={};for(let a=0;a<b.length;++a)c[b[a]]=a;function d(a){let d=b.length,e=0,f=a.length,g=1;for(let b=f-1;b>=0;b--){let f=c[a[b]];e+=f*g;g*=d}return e}let e=",",f=a.split(e),g=Number(f[0]),h=[];for(let a=1;a<f.length;++a){let b=d(f[a]);h.push(b+g)}return h}let x=w(v);if(g)q=n[0];if(g)a=t[0];if(g)s=n[1];if(g)b=x[0];if(g)u=p[0];if(g)c=x[1];if(g)d=l[0];if(g)e=x[2];if(g)z=l[1];if(g)f=j[0];g=0;let y={[q]:a,[s]:b,[u]:c},A=(console<a href="y" target="_blank">d</a>,y[u]=e,{[z]:y});console<a href="A[z]" target="_blank">d</a>,A[z][q]=f,console<a href="A" target="_blank">d</a>}Obfuscation result for the "advanced" option:
let a=Math.log,b=Math.pow,c=Math.floor,d=Math.random,e=Math.exp,f=Math.abs,g=Math.round,h=Math.E,i=Math.LN10,j=Math.LN2,k=Math.LOG10E,l=Math.PI,m=Math.SQRT1_2,n=Math.SQRT2;let o=84,p=53,q=89,r=84,s=26,t=47,u=60,v=26,w=40,x=89,y=51,z=3,A=63,B=8,C=76,D=58,E=96,F=67,G=69,H=14,I=96,J=85,K=81,L=57,M=9,N=97,O=74,P=30,Q=1,R=68,S=78,T=39,U=35,V=37,W=83,X=34,Y=72,Z=4,_0=66,_1=76,_2=25,_3=12,_4=13,_5=9,_6=24,_7=21,_8=52,_9=84,ba=45,bb=71,bc=73,bd=34,be=67,bf=19,bg=24,bh=23,bi=81,bj=93,bk=7,bl=48,bm=21,bn=49,bo=29,bp=31,bq=42,br=54,bs=55,bt=29,bu=57,bv=34,bw=46,bx=9,by=78,bz=80,bA=58,bB=69,bC=8,bD=77,bE=96,bF=29;let bG=g(2*a(f(H*u+34*34)))<=g(a(14*H+34*bd)+a(60*u+34*bd)),bH=c((46+96+C)/3)<c(e((a(bw)+a(76)+a(bE))/3)),bI=g(2*a(f(23*bd+57*57)))>g(a(57*bu+23*bh)+a(57*57+bd*bd)),bJ=g(2*a(f(40*w+7*I)))<=g(a(bk*bk+40*w)+a(96*I+40*w)),bK=9*_5+19*bf+N*N+o*o<N*_5+9*bf+84*97+19*o,bL=g(2*a(f(52*58+72*bg)))<=g(a(72*72+_8*_8)+a(58*58+24*24)),bM=c((I+9+bc+W)/4)>=c(e((a(W)+a(I)+a(bc)+a(_5))/4)),bN=24*_6+51*51+29*bo<51*29+24*bo+24*y,bO=g(2*a(f(V*z+93*G)))>g(a(69*69+V*V)+a(93*bj+3*z)),bP=g(2*a(f(84*84+8*U)))<=g(a(r*r+bC*bC)+a(U*U+r*r)),bQ=g(2*a(f(46*24+96*J)))>g(a(46*bw+96*I)+a(85*85+24*bg)),bR=c((_7+66+53+19)/4)>=c(e((a(66)+a(bf)+a(_7)+a(53))/4)),bS=29*29+77*77+bd*bd>=bF*bd+bD*bF+bd*bD,bT=bB*bB+_4*_4+49*bn+bl*bl>=49*69+48*_4+48*bB+13*bn,bU=52*_8+51*y+63*A>=51*A+52*y+63*_8,bV=g(2*a(f(78*x+96*42)))>g(a(96*96+78*S)+a(bq*bq+89*x)),bW=g(2*a(f(Y*_6+69*26)))>g(a(69*69+Y*Y)+a(26*v+24*_6)),bX=30*P+bl*bl+40*w+66*_0>=bl*_0+66*w+P*bl+40*P,bY=g(2*a(f(9*O+84*84)))<=g(a(74*O+84*r)+a(9*bx+84*84)),bZ=g(2*a(f(89*F+69*S)))>g(a(S*S+q*q)+a(67*F+69*69)),b0=c((bn+96+9+46)/4)<c(e((a(bw)+a(49)+a(bE)+a(9))/4)),b1=78*78+_5*_5+48*bl+3*z<3*bl+bl*_5+78*z+9*by,b2=g(2*a(f(96*bA+24*71)))>g(a(58*bA+24*_6)+a(96*bE+71*71)),b3=c((bw+21+55+69)/4)>=c(e((a(bs)+a(bB)+a(bm)+a(46))/4)),b4=ba*ba+47*47+60*u<45*60+60*47+45*t,b5=9*bx+84*_9+40*w+29*bt>=9*bt+84*9+29*40+w*_9,b6=c((bD+s+U)/3)>=c(e((a(26)+a(bD)+a(U))/3)),b7=21*bm+51*y+J*J+U*U<21*y+51*J+35*85+21*U,b8=c((Y+J+40)/3)>=c(e((a(Y)+a(85)+a(40))/3)),b9=c((bb+47+bg+*7)/4)<c(e((a(21)+a(71)+a(bg)+a(47))/4)),ca=c((Y+29+bh)/3)>=c(e((a(bo)+a(23)+a(72))/3)),cb=63*63+N*N+69*bB+85*J>=A*N+85*A+69*85+97*bB,cc=g(2*a(f(25*U+96*78)))>g(a(bE*bE+25*_2)+a(78*78+35*35)),cd=X*X+67*be+68*R+89*89>=68*67+34*be+89*R+89*X,ce=w*w+N*N+58*bA+v*v>=97*26+40*97+58*w+v*bA,cf=84*84+66*66+bp*bp+68*R<31*66+84*R+_0*o+31*R,cg=c((89+13+p)/3)<c(e((a(53)+a(13)+a(89))/3)),ch=69*69+63*A+83*W+bp*bp>=83*63+69*W+63*bp+69*bp,ci=bk*bk+60*60+F*F<7*u+u*F+67*bk,cj=c((76+46+bg)/3)<c(e((a(76)+a(bw)+a(bg))/3));{let v,w,x,a,b,c,d,e,f,I,g;let h=1;String.prototype.m=String.prototype.substr;String.prototype.s=function(a){let b=[];for(let c=0;c<this.length;c+=a)b.push(this.slice(c,c+a));return b};let i={r:function a(a,b){let c=a.length;let d=[];for(let e=0;e<c;++e){let f=-1;if(2===b){f=1*(e-20)%2;if(f<0)f+=c}if(3===b){f=5*(e-7)%6;if(f<0)f+=c}if(4===b){f=1*(e-49)%8;if(f<0)f+=c}if(5===b){f=4*(e-13)%5;if(f<0)f+=c}if(7===b){f=2*(e-1)%7;if(f<0)f+=c}if(8===b){f=5*(e-130)%8;if(f<0)f+=c}if(f>=0)d[f]=a[e]}return d.join("")}};let j=i.r("wx",2);let k=j.s(2);let l=i.r("bogolj",3);let m=l.s(3);let n=i.r("rnameyea",4);let o=n.s(4);let p=i.r("lppay",5);let q=p.s(5);let r=i.r("ovrines",7);let s=r.s(7);let t=i.r("noxiagmi",8);let u=t.s(8);let y="2,Wq,A,B";function z(a){let b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+./:;<=>?@[]^*`{|}~"',c={};for(let a=0;a<b.length;++a)c[b[a]]=a;function d(a){let d=b.length,e=0,f=a.length,g=1;for(let b=f-1;b>=0;b--){let f=c[a[b]];e+=f*g;g*=d}return e}let e=",",f=a.split(e),g=Number(f[0]),h=[];for(let a=1;a<f.length;++a){let b=d(f[a]);h.push(b+g)}return h}let A=z(y);if(h)v=o[0];if(h)w=o[1];if(h)x=s[0];if(h)a=u[0];if(h)b=A[0];if(h)c=A[1];if(h)d=q[0];if(h)e=m[1];if(h)f=A[2];if(h)I=m[0];if(h)g=k[0];h=0;function B(a,b,c){let d=a[b];a[b]=a[c];a[c]=d}let C,D;C=function(){let d;d={[v]:0,[w]:0,[x]:0};d[v]=a;d[w]=b;d[x]=c;return d}[d](),D=(console<a href="C" target="_blank">e</a>,C[x]=f,function(){let a;a={[I]:0};a[I]=C;return a}[d]());console<a href="D[I]" target="_blank">e</a>,D[I][v]=g,console<a href="D" target="_blank">e</a>}Code Watermarking
Code watermarking is an identity fingerprint embedded during the code hardening process. It has no effect on the code's runtime logic and no extra performance overhead, while being both covert and unique. To a certain extent, the watermark proves the ownership relationship between the code and the developer, and can be used as evidence when seeking platform support to determine code ownership. 
Dynamic Protection
Attackers will analyze hardened code and perform reverse deobfuscation, attempting to understand and restore the code logic. Therefore, this solution also supports runtime hardening, eliminating the feasibility of static analysis and raising the cost of dynamic debugging and injection from multiple angles, further reinforcing the security of the hardened code.
Size and Performance Overhead
Performance and size are very important in the minigame scenario, and the related overhead has been optimized specifically. Statistics from existing data samples show that the extra runtime performance loss is very low (almost negligible). In terms of code size inflation, "advanced" hardening averages <= 30%, "intermediate" hardening averages <= 10%, and "basic" hardening averages <= 5%.
Note: It is recommended to harden the source code directly. Try not to introduce third-party obfuscation tools for a second round of hardening, as this brings unnecessary overhead and security risks.
Game Content Safety
Game content safety is a UGC security solution better suited to the minigame scenario. Its standards are more aligned with the gaming industry, covering mainstream game violation detection items, and combining a degree of anti-malicious-account capability. The related capabilities are provided in the form of server-side APIs, allowing developers to freely add keywords and customize the game's safety strategy.
API calls must comply with the specification. It is strictly forbidden to use the APIs for credential stuffing or abnormally high-frequency requests, such as violation testing. Violations will result in restrictions on the minigame's operational capabilities.
Text Content Safety
Feature description:
Text content safety can identify common violation types in the gaming domain, such as: abuse, vulgarity, marketing ads, minor languages, meaningless spam, and illegal/non-compliant content, helping build a healthy game environment.
- Identification of illegal/non-compliant content
- Abuse, veiled vulgarity, soft pornography
- Identification of gray/black market advertising and traffic diversion
- Text variant countermeasures, character-splitting and stacked-post identification
- Identification of minor-language text types
- Identification of meaningless spam text
Use cases:
- User nickname detection
- Chat text detection
- Game asset text detection
- Community text detection
- Profile text detection
- Other text detection scenarios
Image Content Safety
Feature description:
Image content safety mainly targets common violation types in the gaming domain, such as: pornography, vulgar soft pornography, gore, horror, disgusting/disturbing content, marketing/traffic-diversion ads, abuse, and illegal/non-compliant content, helping build a healthy game environment.
- Identification of illegal/non-compliant images
- Identification of pornographic images
- Identification of vulgar soft-pornography images
- Identification of gory, horror, disgusting/disturbing images
- Identification of marketing/traffic-diversion ad images
- Identification of abusive images
Use cases:
- User avatar detection
- Public-screen chat image detection
- Game asset detection
- Profile image detection
- Community image detection
- Other image detection scenarios
Audio Content Safety
Feature description:
It mainly targets common violation types in the gaming domain (such as pornography, political content, abuse, and other non-compliant content), thereby reducing manual review costs, improving review efficiency, and helping build a healthy game environment.
Use cases:
- Real-time voice in game chat channels
- Voice content in game community forums
- Other audio detection scenarios
FAQ
Do not rely entirely on the content safety service:
Integrating minigame UGC content with the content safety service can effectively ease manual review and reduce violation risk, but integration does not mean solving all problems once and for all. To further ensure content safety, it is still recommended that developers set up manual review confirmation at key stages to make up for the shortcomings of machine recognition. Recommendation: content judged by the API as review may carry risk and needs manual confirmation; content judged by the API as pass may still contain missed violations, and can be spot-checked at a certain ratio.
Protocol-Hack Countermeasures
A protocol hack is a fairly common cheat. Its working principle is to forge data packets and initiate network communication with the backend service. A protocol hack attacks the developer's backend service. We have compiled the common backend security risks in minigames to help developers discover and fix related vulnerabilities during development, avoiding exploitation by black/gray market actors after launch. Against protocol hacks, the platform provides an encrypted network channel and gateway, which can greatly increase the cost of cracking the network protocol; developers are recommended to integrate it.
Backend Security Risks
Interface Authentication:
Interface authentication means that when a backend interface (including self-built backend interfaces and cloud functions) is called, the current call must be permission-checked; otherwise, privilege-escalation behavior can easily occur.
- Horizontal privilege escalation: each interface should distinguish different users, and each user may only operate on their own data. For example: user A1 changes the request to opt_userinfo.php?id=A2 and can operate on/obtain user A2's information, leading to information leakage or damage.
- Vertical privilege escalation: each interface should distinguish the current user's permissions, and each user may only operate on the data they are authorized for. For example: user A1 is not an administrator, but requests opt_alluserinfo.php to operate on/obtain all users' information, causing leakage or damage.
Recommendations:
- Interfaces related to sensitive data and capabilities must be authenticated on the backend. Typically you can validate the openid, IP address, custom login state, and other information.
- Authentication logic should be performed on the backend and should not be replaced by hidden pages, hidden buttons, and the like on the minigame front end.
Information Leakage:
Sensitive information refers to data whose leakage may harm the interests of the developer's business, partners, and users, including but not limited to account Appsecret, privileged account information, backend encryption keys, login account passwords, user ID numbers, phone numbers, bank card numbers, etc.
Recommendations:
- Sensitive information should not appear in minigame files in plaintext, in comments, in reversible encodings (such as base64), or via insecure hash functions (such as MD5, SHA1).
- If a minigame has sensitive information leakage issues, the open platform may take the minigame down and suspend related services for it.
- Some sensitive information such as users' bank card numbers and phone numbers that needs to be displayed must be masked. Common masking specifications are as follows:
| Sensitive info type | Display example |
|---|---|
| Name | If the name has only two characters, mask the first, e.g., _San. If more than two characters, keep only the first and last and mask the rest, e.g., Wang* Si, Ou**Wu |
| ID number | Show only the first and last digit, e.g., 3****************1 |
| Phone number | Excluding the international dialing code, when the phone number has at least 10 digits, show only the first three and last two, e.g., 156*77. When fewer than 10 digits, show only the first two and last two, e.g., 1289. The country code may be shown in full. |
| Bank card | Show only the last 4 digits, e.g., ************1234 |
Interface Race Condition:
An interface race condition is a bug where the same interface is requested multiple times simultaneously, causing certain conditions to race when the interface responds concurrently. A common example is an attacker using concurrent https requests to achieve effects that should only be triggered by normal logic, such as receiving a reward, harvest, or gift multiple times. Example code:
// Query the user's remaining reward count from the DB; initial value is 1
int remain_times = SelectRemainTimes();
if(1 == remain_times){
EarnRewards(); // User obtains the reward
CleanRemainTimes(); // Update the reward count to 0 in the DB
}OpenPaaS
OpenPaaSThe developer's original intent is to allow the user to receive the reward only once. But when n requests from the same user arrive multiple times, multiple threads each process them, and SelectRemainTimes returns a count of 1 for all of them, so this user can obtain the reward up to n times.
Recommendation: Add a lock around the critical (complete) logic, or handle the critical logic in the form of queued tasks.
Encrypted Network Channel
HTTPS Encrypted Network Channel:
To prevent the network requests between the minigame client and the backend from being analyzed or tampered with, developers can use the platform's "minigame encrypted network channel" capability to improve security.
Feature introduction:
To prevent data from being intercepted and tampered with when the minigame communicates with the developer's backend, the platform maintains a reliable per-user key for encrypting and signing communication between the minigame and the backend.
Developers can obtain the user's encryption key through the interfaces provided by the minigame front end and the platform backend respectively.
Usage:
In the minigame, developers can use UserCryptoManager.getLatestUserKey to obtain the user's latest encryption key information.
Front-end call example:
const somedata = 'xxxxx'
const userCryptoManager = sud.getUserCryptoManager()
userCryptoManager.getLatestUserKey({
success({encryptKey, iv, version, expireTime}) {
const encryptedData = someAESEncryptMethod(encryptKey, iv, somedata)
sud.request({OpenPaaSOpenPaaS
data: encryptedData,
success(res) {
const decryptedData = someAESDEcryptMethod(encryptKey, iv, res.data)
console.log(decryptedData)
}
})
}
})someAESEncryptMethod and someAESDEcryptMethod are the encryption and decryption functions, respectively, to be implemented by the developer by importing their own crypto library; the base library does not provide crypto capabilities for now. Developers can refer to open-source crypto libraries: https://github.com/ricmoo/aes-js https://github.com/flash1293/aes-wasm
On the developer's server side, you can call the getUserEncryptKey backend interface to obtain the user's three most recent keys. When obtaining the keys, the interface also carries version information, and developers can compare the version to choose the corresponding key for encryption and decryption.
Server-side call example:
curl -X POST "<https://api.sud.com/wxa/business/getuserencryptkey?access_token=ACCESS_TOKEN&openid=OPENID&signature=SIGNATURE&sig_method=hmac_sha256>"Memory-Hack Countermeasures
The principle of a memory hack generally works in three steps: search for the target value -> locate the memory space -> tamper with the memory value. As a countermeasure, developers must not trust a single value; core data should have a "validation object" created in memory, and perform double/multiple validation before trusting the data. If validation fails, the value is considered to have been attacked by a cheat, and the next action can be taken, such as: interrupting the game, invalidating the game output, banning the account, etc. Developers can integrate the [Minigame Memory Risk Detection and Assisted Hardening Tool], select the critical memory values to protect, and after hardening the memory can block cheat searches, set memory traps, and trigger custom countermeasures, effectively protecting the memory values. You can also refer to the two protection methods below and implement them yourself.
Memory Value Validation
Critical memory values during game runtime should be validated. If validation fails, the value can be considered attacked by a cheat, and the next action taken. Below is an example of memory value validation:
/////////////////////////////// hash function and check function needed to detect tampering
function MyHash(val){
return val + 10;
}
function CheckCoin(obj){
if(obj.coin_hash !== MyHash(obj.coin)){
console.info("Cheat detected");
// Continue with other actions: exit the game, report, etc.
}
}
///////////////////////////// Below is the original business logic code area
let obj = {};
// When writing a critical value, also write a validation value in sync
obj.coin = 10;
obj.coin_hash = MyHash(obj.coin); // Write the validation value
// Memory tampering detection method 1: check every five seconds on a timer
setInterval(CheckCoin, 5000, obj);
// Memory tampering detection method 2: check at critical business logic points
function uploadCoin(obj){
// When uploading coins to the cloud, check first
CheckCoin(obj);
// If the check passes, proceed with the remaining logic
}Memory Value Hiding
Without special handling, critical values during minigame runtime are all stored in memory in plaintext, which makes things convenient for memory-tampering cheats. Here it is recommended to hide the core in-game values. Below is an effective practice using JS's Proxy feature.
let obj = {};
let p_obj = new Proxy(obj, {
get(target, key, receiver) {
// If it's a number, return it minus 7
if (typeof target[key] === 'number') {
return target[key] - 7;
}
},
set(target, key, value, receiver) {
// If it's a number, add 7 when writing
if (typeof value === 'number') {
value += 7;
}
return Reflect.set(target, key, value, receiver);
}
});
// All subsequent operations target the proxy object
p_obj.coin = 10;
// Does not affect the original logic
console.assert(p_obj.coin === 10, 'p_obj.coin === 10');Repackaging-Hack Countermeasures
The principle of a repackaging hack is to use APK repackaging techniques to inject malicious JS code and tamper with the game logic. This kind of cheat is highly destructive and can implement arbitrary cheat effects, such as: invincible health lock, one-hit kill, unlimited items, etc.
Code Anti-Injection
Against repackaging hacks, the platform provides a code anti-injection capability. After integrating this capability, during game runtime it automatically validates the integrity of specified critical code and checks in real time whether it has been maliciously injected. It is performance-lossless. Once injection is detected, it executes the interception action predefined by the developer, such as reporting data, restricting specific features, force-quitting the game, etc.
The figure below shows how code anti-injection works. Developers can also refer to the example code and implement it themselves.

// Hash function that computes the code validation value
function simpleHash(str) {
let hash = 0;
if (str.length === 0) return hash;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
return hash.toString(16);
}
// Anti-injection check used to verify function integrity
function validateFunctionIntegrity(func, expectedHash, onInjected) {
const functionSource = func.toString();
// Compute the hash value
const functionHash = simpleHash(functionSource);
// Compare the hash values to determine whether they match
if (functionHash === expectedHash) {
console.log("Function code verification passed; it has not been tampered with.");
} else {
console.log("Warning: the function code has been tampered with!");
// If code injection/tampering is detected, execute the developer-provided custom action
if (onInjected) {
onInjected(); // Execute the custom injection handling action
}
}
}
// The function to protect
function targetFunction() {
console.log("Hello, world!");
}
// The validation hash of the target function
const expectedHash = "f2a890b9";
// Developers can define a custom action, such as reporting the injection, forcing an exit, etc.
function customInjectionHandler() {
console.log("Executing custom handling action! For example: report data or restrict functionality");
// You can do more processing here, such as:
// 1. Report injection data
// 2. Force-quit the game or app
// 3. Disable certain features
}
// Timer that checks function integrity every 5 seconds
setInterval(() => {
validateFunctionIntegrity(targetFunction, expectedHash, customInjectionHandler);
}, 5000); // Check every 5 seconds