Skip to content

Commit

Permalink
anti spoof changes
Browse files Browse the repository at this point in the history
  • Loading branch information
agl-1984 committed Apr 8, 2024
1 parent 1d7dbc4 commit 009a467
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 10 deletions.
6 changes: 6 additions & 0 deletions Telegram/Resources/langs/lang.strings
Original file line number Diff line number Diff line change
Expand Up @@ -2854,6 +2854,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_url_auth_login_option" = "Log in to {domain} as {user}";
"lng_url_auth_allow_messages" = "Allow {bot} to send me messages";

"lng_open_spoof_title" = "⚠️ URL Spoofing";
"lng_open_spoof_link" = "Do you want to open link that is masked to look like different link?";
"lng_open_spoof_link_confirm" = "It is Safe";
"lng_open_spoof_link_label" = "Link is shown as";
"lng_open_spoof_link_url" = "Actual link is";

"lng_bot_start" = "Start";
"lng_bot_choose_group" = "Select a Group";
"lng_bot_no_groups" = "You have no groups";
Expand Down
120 changes: 119 additions & 1 deletion Telegram/SourceFiles/core/click_handler_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ For license and copyright information please follow this link:
#include "window/window_session_controller_link_info.h"
#include "styles/style_layers.h"

#include <QUrlQuery>

namespace {

// Possible context owners: media viewer, profile, history widget.
Expand Down Expand Up @@ -81,6 +83,83 @@ bool UrlRequiresConfirmation(const QUrl &url) {
RegExOption::CaseInsensitive);
}

bool IsLabelImpersonateUrl(QString url, QString label) {
using namespace qthelp;
if (url == label) {
return false;
}

// avoid ' @username' workaround
label = label.trimmed();
QUrl urlObj(Core::TryConvertUrlToLocal(url));
QUrl labelObj(Core::TryConvertUrlToLocal(label));
if (labelObj.host().isEmpty()) {
static const auto IsHost = QRegularExpression(
"^\\w[\\w\\d_]+\\.\\w[^\\s]+$",
QRegularExpression::CaseInsensitiveOption);
const auto match1 = IsHost.match(labelObj.path());
if (match1.hasMatch()) {
labelObj = QUrl("https://" + label);
}
}

// check if tg username
if (label.startsWith("@")) {
// username?
if (urlObj.scheme() != "tg") {
// external website? ok
return false;
}
}

if (urlObj.scheme() == "tg")
{
if (label.startsWith("@")) {
// check tg name
QUrlQuery args(urlObj);
if (args.queryItemValue("domain").compare(label.mid(1), Qt::CaseInsensitive) != 0) {
return true;
}
}
else if (labelObj.scheme() == "tg") {
// tg:// urls should be exactly the same
return (labelObj != urlObj);
}
}

if (label.startsWith("#")) {
// hash tags should be EntityType::Hashtag, not CustomUrl
return true;
}

if (!labelObj.host().isEmpty()) {
// check matching for schema and domain
if (!labelObj.scheme().isEmpty()
&& (urlObj.scheme() != labelObj.scheme())) {
if ((urlObj.scheme() == "https") && (labelObj.scheme() == "http")) {
// accept this case, but not vice versa
}
else {
// schema is different (tg!=http, or https!=http)
return true;
}
}
if (urlObj.host() != labelObj.host()) {
return true;
}
// urls are different somewhere in path?query - fine
// potentially - accept only shorten version of url
return false;
}
return false;
}

HiddenUrlClickHandler::HiddenUrlClickHandler(QString url, QString label)
: UrlClickHandler(url, false)
, _label(label)
, _isSpoof(IsLabelImpersonateUrl(url, label)) {
}

QString HiddenUrlClickHandler::copyToClipboardText() const {
return url().startsWith(u"internal:url:"_q)
? url().mid(u"internal:url:"_q.size())
Expand All @@ -102,7 +181,7 @@ QString HiddenUrlClickHandler::dragText() const {
return result.startsWith(u"internal:"_q) ? QString() : result;
}

void HiddenUrlClickHandler::Open(QString url, QVariant context) {
void HiddenUrlClickHandler::Open(QString url, QVariant context, bool IsSpoof, QString label) {
url = Core::TryConvertUrlToLocal(url);
if (Core::InternalPassportLink(url)) {
return;
Expand All @@ -111,6 +190,45 @@ void HiddenUrlClickHandler::Open(QString url, QVariant context) {
const auto open = [=] {
UrlClickHandler::Open(url, context);
};

if (IsSpoof) {
Core::App().hideMediaView();
const auto displayed = url;
const auto displayUrl = ShowEncoded(displayed);
const auto my = context.value<ClickHandlerContext>();
const auto controller = my.sessionWindow.get();
const auto use = controller
? &controller->window()
: Core::App().activeWindow();
auto box = Box([=](not_null<Ui::GenericBox*> box) {
Ui::ConfirmBox(box, {
.text = (tr::lng_open_spoof_link(tr::now)),
.confirmed = [=](Fn<void()> hide) { hide(); open(); },
.confirmText = tr::lng_open_spoof_link_confirm(),
.title = (tr::lng_open_spoof_title(tr::now))
});
const auto& st = st::boxLabel;
const auto& stdiv = st::boxDividerLabel;
box->addSkip(st.style.lineHeight - st::boxPadding.bottom());
box->addRow(object_ptr<Ui::FlatLabel>(box, tr::lng_open_spoof_link_label(), stdiv));
const auto url = box->addRow(object_ptr<Ui::FlatLabel>(box, label, st));
box->addRow(object_ptr<Ui::FlatLabel>(box, tr::lng_open_spoof_link_url(), stdiv));
const auto actual_url = box->addRow(object_ptr<Ui::FlatLabel>(box, displayUrl, st));
url->setSelectable(true);
url->setContextCopyText(tr::lng_context_copy_link(tr::now));
actual_url->setSelectable(true);
actual_url->setContextCopyText(tr::lng_context_copy_link(tr::now));
});
if (my.show) {
my.show->showBox(std::move(box));
}
else if (use) {
use->show(std::move(box));
use->activate();
}
return;
}

if (url.startsWith(u"tg://"_q, Qt::CaseInsensitive)
|| url.startsWith(u"internal:"_q, Qt::CaseInsensitive)) {
UrlClickHandler::Open(url, QVariant::fromValue([&] {
Expand Down
10 changes: 6 additions & 4 deletions Telegram/SourceFiles/core/click_handler_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,24 @@ Q_DECLARE_METATYPE(ClickHandlerContext);

class HiddenUrlClickHandler : public UrlClickHandler {
public:
HiddenUrlClickHandler(QString url) : UrlClickHandler(url, false) {
}
HiddenUrlClickHandler(QString url, QString label);
QString copyToClipboardText() const override;
QString copyToClipboardContextItemText() const override;
QString dragText() const override;

static void Open(QString url, QVariant context = {});
static void Open(QString url, QVariant context = {}, bool IsSpoof = false, QString label = QString());
void onClick(ClickContext context) const override {
const auto button = context.button;
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
Open(url(), context.other);
Open(url(), context.other, _isSpoof, _label);
}
}

TextEntity getTextEntity() const override;

private:
QString _label;
bool _isSpoof;
};

class UserData;
Expand Down
4 changes: 2 additions & 2 deletions Telegram/SourceFiles/core/ui_integration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,12 @@ std::shared_ptr<ClickHandler> UiIntegration::createLinkHandler(
case EntityType::Url:
return (!data.data.isEmpty()
&& UrlClickHandler::IsSuspicious(data.data))
? std::make_shared<HiddenUrlClickHandler>(data.data)
? std::make_shared<HiddenUrlClickHandler>(data.data, data.data)
: Integration::createLinkHandler(data, context);

case EntityType::CustomUrl:
return !data.data.isEmpty()
? std::make_shared<HiddenUrlClickHandler>(data.data)
? std::make_shared<HiddenUrlClickHandler>(data.data, data.text)
: Integration::createLinkHandler(data, context);

case EntityType::BotCommand:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ QSize WebPage::countOptimalSize() {
? IvClickHandler(_data, original)
: (previewOfHiddenUrl || UrlClickHandler::IsSuspicious(
_data->url))
? std::make_shared<HiddenUrlClickHandler>(_data->url)
? std::make_shared<HiddenUrlClickHandler>(_data->url, _data->url)
: std::make_shared<UrlClickHandler>(_data->url, true);
if (_data->document
&& (_data->document->isWallPaper()
Expand Down
4 changes: 2 additions & 2 deletions Telegram/SourceFiles/overview/overview_layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1611,7 +1611,7 @@ Link::Link(

const auto createHandler = [](const QString &url) {
return UrlClickHandler::IsSuspicious(url)
? std::make_shared<HiddenUrlClickHandler>(url)
? std::make_shared<HiddenUrlClickHandler>(url, url)
: std::make_shared<UrlClickHandler>(url, false);
};
_page = media ? media->webpage() : nullptr;
Expand Down Expand Up @@ -1941,7 +1941,7 @@ Link::LinkEntry::LinkEntry(const QString &url, const QString &text)
: text(text)
, width(st::normalFont->width(text))
, lnk(UrlClickHandler::IsSuspicious(url)
? std::make_shared<HiddenUrlClickHandler>(url)
? std::make_shared<HiddenUrlClickHandler>(url, text)
: std::make_shared<UrlClickHandler>(url)) {
}

Expand Down

0 comments on commit 009a467

Please sign in to comment.