GramJS: Add more security checks (#3419)
This commit is contained in:
parent
6d8f415bc1
commit
823fd9add7
@ -100,7 +100,7 @@ export function getUserStatus(
|
|||||||
if (!wasOnline) return lang('LastSeen.Offline');
|
if (!wasOnline) return lang('LastSeen.Offline');
|
||||||
|
|
||||||
const serverTimeOffset = getServerTimeOffset();
|
const serverTimeOffset = getServerTimeOffset();
|
||||||
const now = new Date(new Date().getTime() + serverTimeOffset * 1000);
|
const now = new Date(Date.now() + serverTimeOffset * 1000);
|
||||||
const wasOnlineDate = new Date(wasOnline * 1000);
|
const wasOnlineDate = new Date(wasOnline * 1000);
|
||||||
|
|
||||||
if (wasOnlineDate >= now) {
|
if (wasOnlineDate >= now) {
|
||||||
|
|||||||
@ -291,7 +291,7 @@ class TelegramClient {
|
|||||||
// for Telegram to keep delivering updates, otherwise they will
|
// for Telegram to keep delivering updates, otherwise they will
|
||||||
// just stop even if we're connected. Do so every 30 minutes.
|
// just stop even if we're connected. Do so every 30 minutes.
|
||||||
|
|
||||||
if (new Date().getTime() - this._lastRequest > 30 * 60 * 1000) {
|
if (Date.now() - this._lastRequest > 30 * 60 * 1000) {
|
||||||
try {
|
try {
|
||||||
await this.pingCallback();
|
await this.pingCallback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -877,7 +877,7 @@ class TelegramClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sender = dcId === undefined ? this._sender : await this.getSender(dcId);
|
const sender = dcId === undefined ? this._sender : await this.getSender(dcId);
|
||||||
this._lastRequest = new Date().getTime();
|
this._lastRequest = Date.now();
|
||||||
|
|
||||||
const state = new RequestState(request, abortSignal);
|
const state = new RequestState(request, abortSignal);
|
||||||
|
|
||||||
|
|||||||
@ -156,11 +156,15 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
|
|||||||
const ige = new IGE(key, iv);
|
const ige = new IGE(key, iv);
|
||||||
const plainTextAnswer = ige.decryptIge(serverDhParams.encryptedAnswer);
|
const plainTextAnswer = ige.decryptIge(serverDhParams.encryptedAnswer);
|
||||||
const reader = new BinaryReader(plainTextAnswer);
|
const reader = new BinaryReader(plainTextAnswer);
|
||||||
reader.read(20); // hash sum
|
const hash = reader.read(20); // hash sum
|
||||||
const serverDhInner = reader.tgReadObject();
|
const serverDhInner = reader.tgReadObject();
|
||||||
if (!(serverDhInner instanceof Api.ServerDHInnerData)) {
|
if (!(serverDhInner instanceof Api.ServerDHInnerData)) {
|
||||||
throw new Error(`Step 3 answer was ${serverDhInner}`);
|
throw new Error(`Step 3 answer was ${serverDhInner}`);
|
||||||
}
|
}
|
||||||
|
const sha1Answer = await Helpers.sha1(serverDhInner.getBytes());
|
||||||
|
if (!(hash.equals(sha1Answer))) {
|
||||||
|
throw new SecurityError('Step 3 Invalid hash answer');
|
||||||
|
}
|
||||||
|
|
||||||
if (serverDhInner.nonce.neq(resPQ.nonce)) {
|
if (serverDhInner.nonce.neq(resPQ.nonce)) {
|
||||||
throw new SecurityError('Step 3 Invalid nonce in encrypted answer');
|
throw new SecurityError('Step 3 Invalid nonce in encrypted answer');
|
||||||
@ -185,7 +189,7 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
const ga = Helpers.readBigIntFromBuffer(serverDhInner.gA, false, false);
|
const ga = Helpers.readBigIntFromBuffer(serverDhInner.gA, false, false);
|
||||||
const timeOffset = serverDhInner.serverTime - Math.floor(new Date().getTime() / 1000);
|
const timeOffset = serverDhInner.serverTime - Math.floor(Date.now() / 1000);
|
||||||
const b = Helpers.readBigIntFromBuffer(
|
const b = Helpers.readBigIntFromBuffer(
|
||||||
Helpers.generateRandomBytes(256),
|
Helpers.generateRandomBytes(256),
|
||||||
false,
|
false,
|
||||||
|
|||||||
@ -284,6 +284,16 @@ class MTProtoState {
|
|||||||
// reader isn't used for anything else after this, it's unnecessary.
|
// reader isn't used for anything else after this, it's unnecessary.
|
||||||
const obj = reader.tgReadObject();
|
const obj = reader.tgReadObject();
|
||||||
|
|
||||||
|
// We only check for objects that telegram has returned to us (Updates) not ones we send.
|
||||||
|
if (obj?.constructor.name.startsWith('Update')) {
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const msgLocalTime = this.getMsgIdTimeLocal(remoteMsgId);
|
||||||
|
|
||||||
|
if (msgLocalTime && ((msgLocalTime - now) > 30 || (now - msgLocalTime) > 300)) {
|
||||||
|
// 30 sec in the future or 300 sec in the past
|
||||||
|
throw new SecurityError('The message time is incorrect.');
|
||||||
|
}
|
||||||
|
}
|
||||||
return new TLMessage(remoteMsgId, remoteSequence, obj);
|
return new TLMessage(remoteMsgId, remoteSequence, obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -294,7 +304,7 @@ class MTProtoState {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_getNewMsgId() {
|
_getNewMsgId() {
|
||||||
const now = new Date().getTime() / 1000 + this.timeOffset;
|
const now = Date.now() / 1000 + this.timeOffset;
|
||||||
const nanoseconds = Math.floor((now - Math.floor(now)) * 1e9);
|
const nanoseconds = Math.floor((now - Math.floor(now)) * 1e9);
|
||||||
let newMsgId = (BigInt(Math.floor(now))
|
let newMsgId = (BigInt(Math.floor(now))
|
||||||
.shiftLeft(BigInt(32))).or(BigInt(nanoseconds)
|
.shiftLeft(BigInt(32))).or(BigInt(nanoseconds)
|
||||||
@ -306,6 +316,17 @@ class MTProtoState {
|
|||||||
return newMsgId;
|
return newMsgId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the understood time by the message id (server time + local offset)
|
||||||
|
*/
|
||||||
|
getMsgIdTimeLocal(msgId) {
|
||||||
|
if (this._lastMsgId.eq(0)) {
|
||||||
|
// this means it's the first message sent/received so don't check yet
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return msgId.shiftRight(BigInt(32)).toJSNumber() - this.timeOffset;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the time offset to the correct
|
* Updates the time offset to the correct
|
||||||
* one given a known valid message ID.
|
* one given a known valid message ID.
|
||||||
@ -314,7 +335,7 @@ class MTProtoState {
|
|||||||
updateTimeOffset(correctMsgId) {
|
updateTimeOffset(correctMsgId) {
|
||||||
const bad = this._getNewMsgId();
|
const bad = this._getNewMsgId();
|
||||||
const old = this.timeOffset;
|
const old = this.timeOffset;
|
||||||
const now = Math.floor(new Date().getTime() / 1000);
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const correct = correctMsgId.shiftRight(BigInt(32));
|
const correct = correctMsgId.shiftRight(BigInt(32));
|
||||||
this.timeOffset = correct - now;
|
this.timeOffset = correct - now;
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ class MTProtoRequest {
|
|||||||
|
|
||||||
// these should not be overrode
|
// these should not be overrode
|
||||||
onSendSuccess() {
|
onSendSuccess() {
|
||||||
this.sendTime = new Date().getTime();
|
this.sendTime = Date.now();
|
||||||
this.sent = true;
|
this.sent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ class MTProtoRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
needResend() {
|
needResend() {
|
||||||
return this.dirty || (this.confirmed && !this.confirmReceived && new Date().getTime() - this.sendTime > 3000);
|
return this.dirty || (this.confirmed && !this.confirmReceived && Date.now() - this.sendTime > 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// These should be overrode
|
// These should be overrode
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user