**rdpSafe** Windows RDP 3389端口 实现二次验证
Remote Desktop Protocol 的体验确实好用
安全性也受了很大的挑战, Windows Server 2016以上的版本其实已经支持了MFA
验证功能,但不论是从配置和操作,还是文档 资料上都不太好找,因此想了一个简单的办法,增强安全性.
原理 端口转发
通过转发3389端口的数据,实现类似网关的功能,但在转发数据之前进行通知验证
1.客户端请求RDP服务器端口:12345
2.服务器发送二次验证到管理员手机批准
3.通过批准的请求,进行端口转发, 没有批准的请求直接断开链接
端口转发代码如下:
https://www.cnblogs.com/nanfei/p/14109709.html
class Program
{
static bool resetFlag = true;
static readonly object resetFlagLocker = new object();
static void Main(string[] args)
{
var localSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
localSocket.Bind(new IPEndPoint(IPAddress.Any, 20000));
localSocket.Listen(10);
while (true)
{
try
{
lock (resetFlagLocker)
{
if (!resetFlag)
continue;
resetFlag = false;
}
var client1 = localSocket.Accept();
client1.SendTimeout = 300000;
client1.ReceiveTimeout = 300000;
//-------------改动部分------------
// GetData(ip) 表示向手机端请求确认
do
{
if (DateTime.Now > start.AddSeconds(50))
{
_logger.LogWarning($"拒绝连接 ,RemoteIP:{ip}");
resetFlag = true;
client1.Close();
return;
}
Task.Delay(1000).Wait();
r = GetData(ip);
} while (r == 0);
//------------改动部分-------------
var client2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client2.Connect("localhost",3389);
object obj1 = (object)(new Socket[] { client1, client2 });
object obj2 = (object)(new Socket[] { client2, client1 });
ThreadPool.QueueUserWorkItem(new WaitCallback(transfer), obj1);
ThreadPool.QueueUserWorkItem(new WaitCallback(transfer), obj2);
Console.WriteLine("Transfer started");
}
catch (Exception ex)
{
Console.WriteLine("Main error:" + ex.Message);
}
}
}
public static void transfer(object obj)
{
var socketA = ((Socket[])obj)[0];
var socketB = ((Socket[])obj)[1];
while (true)
{
try
{
var data = new byte[10240];
int read = socketA.Receive(data);
socketB.Send(data.Take(read).ToArray());
}
catch (Exception ex)
{
lock (resetFlagLocker)
{
resetFlag = true;
}
Console.Write("Transfer error:" + ex.Message);
try { socketA.Close(); } catch {}
try { socketB.Close(); } catch {}
break;
}
}
}
}
c# GetData函数
//key表示ip地址,或者 GUID 都可以
private int GetData(string key)
{
var r = new Random().Next(10000, 99999);
var url = $"https://locaholost/api/safe/GetSafe?key={key}&t={r}";
var res = new HttpClient().GetStringAsync(url).Result;
return res == "1" ? 1 : 0;
//1表示通过批准,这里可以自定义
}
服务端函数,可以不做持久化存储
[Route("api/[controller]/[action]")]
[ApiController]
[AllowAnonymous]
public class SafeController : ControllerBase
{
private readonly IMemoryCache _cache;
public SafeController(IMemoryCache cache, ICorpMsgService corpMsg)
{
_cache = cache;
}
[HttpGet]
public async Task<int> GetSafe(string key)
{
if (!_cache.TryGetValue(key, out bool value))
{
var rst = _cache.Set(key, false, TimeSpan.FromSeconds(60));
//发送通知
SendSafeMsg($"正在<a href=\"{url}\">{key}请求远程桌面登录,点击批准\r\n</a>");
}
return value ? 1 : 0;
//表示批准
}
[HttpGet]
public int SetSafe(string key)
{
//60秒内有效
var rst = _cache.Set(key, true, TimeSpan.FromSeconds(60));
return rst ? 1 : 0;
}
}
效果模拟链接
2024.01.11