Skip to content

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:

java
    {

        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:

java
    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:

java
    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:

java
  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. Diagram

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 typeDisplay example
NameIf 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 numberShow only the first and last digit, e.g., 3****************1
Phone numberExcluding 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 cardShow 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:

java
    // 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

    OpenPaaS

The 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:

java
    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:

java
    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:

java
    /////////////////////////////// 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.

java
    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.

Diagram

java
    // 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