{"id":16275,"date":"2017-06-28T07:18:23","date_gmt":"2017-06-28T14:18:23","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vcblog\/?p=16275"},"modified":"2019-02-18T17:48:25","modified_gmt":"2019-02-18T17:48:25","slug":"security-features-in-microsoft-visual-c","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/security-features-in-microsoft-visual-c\/","title":{"rendered":"Security Features in MSVC"},"content":{"rendered":"<p>Shareable link: <a href=\"https:\/\/aka.ms\/msvcsecurity\">https:\/\/aka.ms\/msvcsecurity<\/a>\n<a href=\"https:\/\/blogs.msdn.microsoft.com\/c\/2017\/07\/14\/microsoft-visual-c-%E4%B8%AD%E7%9A%84%E5%AE%89%E5%85%A8%E5%8A%9F%E8%83%BD\/\">\u70b9\u8fd9\u91cc\u770b\u4e2d\u6587\u7248<\/a><\/p>\n<p>Every developer makes mistakes. No matter how careful you are when writing code, you will introduce bugs. And any bug can become a security vulnerability when software that runs in a connected environment or is used long past its initially planned lifespan. Code that isn&#8217;t correct is insecure code.<\/p>\n<p>The Microsoft Visual C++ toolset offers numerous features that help you write secure, correct code from before you start typing code until after you\u2019ve shipped it to your users.<\/p>\n<p>For more information on specific security features in the MSVC toolset, make sure you review <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/security\/security-best-practices-for-cpp\">Security Best Practices for C++<\/a>.<\/p>\n<h3>Before you write any code<\/h3>\n<p>Secure code starts before you write your first line of code. The compiler toolset can\u2019t show you design defects that might lead to security exploits, but there are many resources in print and online that will help you think about potential exploits and how to design your code securely. For example, almost everyone who\u2019s been at Microsoft for a while has read Michael Howard and David LeBlanc&#8217;s <a href=\"https:\/\/www.microsoftpressstore.com\/store\/writing-secure-code-9780735617223\">Writing Secure Code<\/a>.<\/p>\n<p>When you start writing code it\u2019s important that you use modern C++ constructs to manage and access resources. One of the best resources available is the <a href=\"https:\/\/github.com\/isocpp\/CppCoreGuidelines\">C++ Core Guidelines<\/a>, a set of tried-and-true guidelines, rules, and best practices about coding in C++. Coding practices recommended in the C++ Core Guidelines help you write simpler, more modern, software. In doing so, you&#8217;ll avoid common pitfalls such as integer overflow or buffer overruns, making your code more secure. And many of the C++ Core Guidelines are enforceable with a <a href=\"#CodeAnalysis\">static analysis code tool that\u2019s included with Visual C++<\/a>.<\/p>\n<h3>When you&#8217;re writing code<\/h3>\n<p>What can you do to help yourself when you&#8217;re writing code? First, get all the value you can from built-in compiler diagnostics by setting your warning levels properly. Run code analysis after you build to let the compiler toolset dive into a deeper analysis of your code. And don\u2019t forget to do <a href=\"https:\/\/www.visualstudio.com\/en-us\/docs\/git\/pull-requests\">regular code reviews with your team<\/a>!<\/p>\n<h4>Compiler Warnings<\/h4>\n<p>One of the most frequently-used security features is compiler warnings. The MSVC compiler provides many switches that allow you to control which warnings you will see in your code and whether they are kept as informational messages or cause your compile to fail.<\/p>\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/preprocessor\/compiler-warnings-that-are-off-by-default\">Some compiler warnings are kept off-by-default<\/a> because they are emitted too frequently in legacy code and most users don\u2019t want to see them. But many of these warnings indicate real bugs in your program. For example, your code may have a valid reason to <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/error-messages\/compiler-warnings\/compiler-warning-level-3-c4287\">compare an unsigned value to a negative number<\/a> but it could also be a bug. By enabling the off-by-default warnings you can catch potential errors.<\/p>\n<p>To learn more about how you can adjust build settings to allow the compiler to find as many bugs in your code as possible, see the <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/compiler-option-warning-level\">documentation on the compiler options warning level<\/a>.<\/p>\n<p><a><\/a><\/p>\n<h4>Static Code Analysis Security Features<\/h4>\n<p>We write frequently about C++ Code Analysis on this blog. We also keep you updated about the CppCoreCheck extension that checks your code for rules derived from the C++ Core Guidelines. But did you know that Microsoft has long considered PREfast, the engine at the core of our Code Analysis, a security tool? The tool was originally developed by a team that focused on software excellence and was later owned by the Secure Development Lifecycle team before making its way to the C++ team to be included with all versions of Visual Studio.<\/p>\n<p>We now have a number of Code Analysis tools built upon the PREfast engine, including our <a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/code-quality\/code-analysis-for-c-cpp-warnings\">base set of \/analyze rules<\/a>, the <a href=\"http:\/\/www.cs.cmu.edu\/~fp\/courses\/15122-f10\/lectures\/25-sal.pdf\">ESPC Concurrency Checker<\/a> (pdf) and the <a href=\"https:\/\/devblogs.microsoft.com\/cppblog\/cppcorecheck\/\">CppCoreCheckers<\/a>. We are also looking for ways to help you <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=VSIDEDevOpsMSFT.ContinuousDeliveryToolsforVisualStudio\">integrate Code Analysis more deeply into your daily development routine<\/a>.<\/p>\n<p>As the name implies, code analysis does a deeper analysis of your code to find possible errors. While compiler detects many potential errors in your code, code analysis looks through an entire function to determine whether there are some code paths that could result in an error. We call this kind of analysis a \u201cpath-sensitive\u201d analysis.<\/p>\n<p>While the compiler can do a lot of path-sensitive analysis, there are many cases it can\u2019t identify. For example, compiling this code with all compiler warnings turned on (<code><a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/compiler-option-warning-level\">\/Wall<\/a><\/code>) and analysis (<code><a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/analyze-code-analysis\">\/analyze<\/a><\/code>) shows that the compiler can only find one of three potential bugs:<\/p>\n<pre class=\"prettyprint\">\nvoid one()\n{\n    int a[4];\n    a[4] = 1; \/\/ Buffer overrun, stack overflow\n}\n\nvoid two(int *p)\n{\n   bool isnull = false;\n   if (p == nullptr)\n      isnull = true;\n   *p = 1;   \/\/ Null pointer dereference\n}\n\nint three(bool b)  \n{  \n   int i;  \n   if (b)  \n      i = 0;  \n   return i; \/\/ i is unintialized if b is false  \n}\n<\/pre>\n<pre class=\"prettyprint disable-colors\">\nC:\\tmp&gt;cl \/c example.cpp \/Wall \/analyze\nMicrosoft (R) C\/C++ Optimizing Compiler Version 19.10.25019 for x64\nCopyright (C) Microsoft Corporation.  All rights reserved.\n\nexample.cpp\nc:\\tmp\\example.cpp(4) : warning C6201: Index '4' is out of valid index range '0' to '3' for possibly stack allocated buffer 'a'.\nc:\\tmp\\example.cpp(4) : warning C6386: Buffer overrun while writing to 'a':  the writable size is '16' bytes, but '20' bytes might be written.: Lines: 3, 4\nc:\\tmp\\example.cpp(12) : warning C6011: Dereferencing NULL pointer 'p'. : Lines: 9, 10, 11, 12\nc:\\tmp\\example.cpp(22) : warning C6001: Using uninitialized memory 'i'.: Lines: 17, 18, 22\nc:\\tmp\\example.cpp(4) : warning C4789: buffer 'a' of size 16 bytes will be overrun; 4 bytes will be written starting at offset 16\nc:\\tmp\\example.cpp(22) : warning C4701: potentially uninitialized local variable 'i' used\n<\/pre>\n<p>There&#8217;s an error in each function in the source above. The compiler misses one of these three errors and indirectly attributes another. As the <a href=\"https:\/\/blogs.msdn.microsoft.com\/vcblog\/2015\/09\/25\/rejuvenating-the-microsoft-cc-compiler\/\">parse information available to the compiler improves<\/a>, the analysis in the compiler will improve and you&#8217;ll see more instances where diagnostics can be duplicated between tools.<\/p>\n<ol>\n<li>In function <code>one<\/code> code analysis tells us that we&#8217;re using the bounds of the array as an index, causing <a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/code-quality\/c6201\">C6201<\/a>. Both code analysis and the compiler pick up on the memory corruption, the former emitting <a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/code-quality\/c6386\">C6386<\/a>, the latter, <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/error-messages\/compiler-warnings\/compiler-warning-level-1-c4789\">C4789<\/a>.<\/li>\n<li>In function <code>two<\/code> we dereference of a null pointer on one code path, causing <a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/code-quality\/c6011\">C6011<\/a>. The compiler misses this error entirely.<\/li>\n<li>The compiler and code analysis both pick up on the error in function <code>three<\/code>. The compiler issues off-by-default warning <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/error-messages\/compiler-warnings\/compiler-warning-level-4-c4701\">C4701<\/a>; code analysis, <a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/code-quality\/c6001\">C6001<\/a>.<\/li>\n<\/ol>\n<p>Code analysis also excels at finding code that doesn\u2019t do what you may think it does. For example, warning C6268 finds code that contains an incorrect order of operations and suggests using parentheses to make the order clear. The <a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/code-quality\/c6268\">example given in the documentation<\/a> contains a potential buffer overrun.<\/p>\n<p>You can read more about <a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/code-quality\/code-analysis-for-c-cpp-overview\">using C++ Code Analysis both inside VS and from the command line<\/a> on the Microsoft Docs site.<\/p>\n<h4>Code Reviews<\/h4>\n<p>Code reviews can seem like a lot of overhead but they save time in the long run. Some teams do one-on-one code reviews, others send out all changes to a group of reviews, some bring the team together every Friday to look over all the week\u2019s changes. It doesn\u2019t matter how you do code reviews. They will turn out to be one of the most valuable techniques you can use to improve the quality of your code. Find a process that works for your team and use it.<\/p>\n<h4>Additional Security Checks<\/h4>\n<p>The <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/sdl-enable-additional-security-checks\">\/sdl compiler switch<\/a> enables additional warnings focused on security issues as defined by the <a href=\"https:\/\/www.microsoft.com\/en-us\/sdl\/default.aspx\">Microsoft Secure Development Lifecycle<\/a> process. The \/sdl switch is in many ways an expansion of off-by-default warning <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/error-messages\/compiler-warnings\/compiler-warning-level-4-c4701\">C4701<\/a> and is thus off by default.<\/p>\n<h4>CRT Secure Function Overloads<\/h4>\n<p>Security wasn&#8217;t an important design point for the C library\u2014normally code was written and run inside of an organization instead of being exposed to a worldwide network of computers. The C &#8220;string&#8221; has no metadata associated with it that records its length. For example, functions that deal with strings, such as <code>strcpy<\/code>, must assume that the buffers supplied as parameters are an appropriate size for the requested operation. Several memory operations have similar limitations.<\/p>\n<p>Over ten years ago Microsoft introduced a set of overloads to these functions that <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/c-runtime-library\/security-features-in-the-crt\">validate their parameters for security<\/a>. If you are still using C-style functions you should consider moving to C++, which offers more safety in its objects and abstractions. If you can&#8217;t use C++, at least <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/c-runtime-library\/security-enhanced-versions-of-crt-functions\">use the secure versions of the C runtime functions<\/a>.<\/p>\n<p>(NB: When we introduced this feature we incorrectly called the insecure C functions &#8220;deprecated&#8221;. This only means that Microsoft does not recommend use of the insecure functions, instead recommending you use the secure overloads. We&#8217;re aware that the term &#8220;deprecated&#8221; was used incorrectly.)<\/p>\n<h3>When you\u2019re testing your code<\/h3>\n<p>The compiler toolset offers many options that help you when you\u2019re testing your code. Most of these switches aren\u2019t intended to be shipped with your final, retail builds of your program. They\u2019re turned on for debug builds\u2014either by default or opt-in\u2014so that you can find more bugs during your testing.<\/p>\n<h4>CRT Debug Heap<\/h4>\n<p>The CRT Debug Heap is enabled when you compile your program in debug (non-release) mode. It finds common heap memory errors, including buffer overruns and leaks. The CRT Debug Heap will assert when it encounters any heap memory errors as you test your code. <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/c-runtime-library\/debug-routines\">Various debug routines<\/a> are enabled when you define the <code>_DEBUG<\/code> flag.<\/p>\n<h4>Runtime Checks<\/h4>\n<p>The CRT provides <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/rtc-run-time-error-checks\">Runtime Checks<\/a> enabled through use of the <code>\/RTC<\/code> switch. These checks find real logic errors in your program such as data loss, initialization issues, and stack frame checking. Runtime Checks are only intended for when you are active testing your debug builds and are incompatible with optimizations. Because of these limitations they are off by default.<\/p>\n<h4>Checked Iterators<\/h4>\n<p>Checked iterators help ensure that your code doesn\u2019t accidentally overwrite the bounds of iterable containers in your code. They can be used both in <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/standard-library\/debug-iterator-support\">debug code<\/a> (as debug iterators) and in <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/standard-library\/checked-iterators\">release code<\/a> (as checked iterators.)<\/p>\n<h3>After your code is compiled<\/h3>\n<p>The Windows team provides tools that help validate that your compiled binaries are secure. You can find these tools in the <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows-hardware\/drivers\/debugger\/debugging-tools-for-windows--new-for-windows-10\">Debugging Tools for Windows<\/a> and the <a href=\"https:\/\/developer.microsoft.com\/en-us\/windows\/downloads\/windows-10-sdk\">Windows SDK<\/a>.<\/p>\n<h4>GFlags and PageHeap<\/h4>\n<p>The <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows-hardware\/drivers\/debugger\/gflags-and-pageheap\">GFlags and PageHeap tools<\/a> enable heap allocation monitoring for Windows. When using these tools, Windows will reserve memory at the boundary of every allocation that allows it to detect memory accesses outside of the allocated memory.<\/p>\n<h4>Application Verifier<\/h4>\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows-hardware\/drivers\/debugger\/application-verifier\">Application Verifier<\/a> is a dynamic verification tool that subjects your binary to a number of stresses and tests as you exercise the code and generates a report of potential vulnerabilities.<\/p>\n<h3>Runtime protection for released code<\/h3>\n<p>The MSVC code generator and linker provide several security features that continue to offer protection long after you\u2019ve built and deployed your code. Because the code generator can see all of your code at once&#8211;as opposed to just one source file at a time&#8211;it can often detect errors and vulnerabilities that can\u2019t be found in an individual source file. And the code generator and linker work with the OS loader and runtime to provide even more security when your binary is loaded and executed in Windows.<\/p>\n<h4>Buffer Security Check<\/h4>\n<p>One of the oldest security features in the code generator is the <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/gs-buffer-security-check\">Buffer Security Check<\/a>, enabled by the <code>\/GS<\/code> switch to the compiler. This feature is on-by-default as it protects against one of the most common security exploits. It creates a &#8220;security cookie&#8221; in functions that the compiler detects are vulnerable to buffer overruns. If an attacker writes past the end of the buffer over a return address, the address of an exception handler, or a vulnerable function parameter they will overwrite the security cookie. The runtime will check the integrity of the cookie before allowing execution to jump to this address or before returning these parameters.<\/p>\n<h4>Safe Exception Handlers<\/h4>\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/safeseh-image-has-safe-exception-handlers\">Safe Exception Handlers<\/a> are another long-standing security feature. This feature is on-by-default but only applies to code generated for the x86 platform. When it is enabled, the linker will only produce an image if it can create a static table of the image&#8217;s safe exception handlers. This prevents an attacker from overwriting the target of exception handling control flow.<\/p>\n<h4>Dynamic Base and Address Space Layout Randomization<\/h4>\n<p><a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/2007.04.vistakernel.aspx\">Address Space Layout Randomization<\/a> (ASLR) is a technique that makes it harder for an attacker to predict target addresses for their attacks. When ASLR is enabled on your binary image the OS loader will load the image at a hard-to-predict base address. The <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/dynamicbase-use-address-space-layout-randomization\"><code>\/DYNAMICBASE<\/code> switch<\/a> to the linker, which is on by default, enables the image to use ASLR.<\/p>\n<h4>Data Execution Prevention<\/h4>\n<p>One common technique for attackers is to use data as executable code. Executing data that has been specially formatted as machine code is a powerful technique used by many languages, like .NET languages or JavaScript, in their Just-In-Time (JIT) compilers. But a C++ program shouldn&#8217;t normally need to execute data. Windows allows data sections to be marked as non-executable using a technique called <a href=\"https:\/\/blogs.technet.microsoft.com\/srd\/2009\/06\/12\/understanding-dep-as-a-mitigation-technology-part-1\/\">Data Execution Protection (DEP)<\/a>. The <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/nxcompat-compatible-with-data-execution-prevention\"><code>\/NXCOMPAT<\/code><\/a> linker switch, on by default, specifies whether an image is compatible with DEP.<\/p>\n<h4>Control Flow Guard<\/h4>\n<p>In 2014 we announced an exciting new security feature called <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/guard-enable-control-flow-guard\">Control Flow Guard<\/a>. The <code>\/guard:cf<\/code> option instructs the compiler to analyze control flow for any indirect calls at compile time and records the results of that analysis in the compiled binary. It also puts a check in the binary before every indirect call that is checked by Windows when your code is run. Windows will call <code>RaiseFastFailException<\/code> if any of these checks fails at runtime.<\/p>\n<h4>Upcoming Security Features<\/h4>\n<p>We continue to innovate with new security features that benefit from our code generator\u2019s program analysis. Security requires \u201c<a href=\"https:\/\/technet.microsoft.com\/library\/cc767969.aspx\">defense in depth<\/a>\u201d because attackers will always find a way to work around the protections you have in place now. We constantly have to find new ways to help protect your code at all levels.<\/p>\n<h3>Is your code secure?<\/h3>\n<p>Good developer tools can do a lot to help you write solid and secure code but unfortunately, they can\u2019t do everything for you. You need to start with a good design that includes security appropriate for the environment our code will run in\u2014both when you deploy it and, potentially, for many years in the future, long after you might have expected your code to be rewritten, replaced, or just obsolete. Can your code be running in a connected environment? You need to plan for attacks, including those as simple as denial of service. Will your code handle sensitive user information? You need to plan for how your code will stand up to attackers who want to get at the data you handle.<\/p>\n<p>Security isn\u2019t a feature that can be bolted onto a finished product. But good tools&#8211;such as those provided in the Visual C++ toolset&#8211;can help you write solid, secure code.<\/p>\n<h4>Thank you!<\/h4>\n<p>Thank you for reading over this long list of security features provided at different points in your development process. And thanks to the hundreds of people who provide feedback and help us improve the C++ experience in Visual Studio.<\/p>\n<p>If you have any feedback or suggestions for us, please reach out. We can be reached via the comments below, via email (<a href=\"mailto:visualcpp@microsoft.com\">visualcpp@microsoft.com<\/a>) and you can provide feedback via <a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/ide\/how-to-report-a-problem-with-visual-studio-2017\">Help &gt; Report A Problem in the product<\/a>, or via <a href=\"https:\/\/developercommunity.visualstudio.com\/topics\/C%2B%2B.html\">Developer Community<\/a>. You can also find us on Twitter (<a href=\"https:\/\/twitter.com\/visualc\">@VisualC<\/a>) and Facebook (<a href=\"https:\/\/www.facebook.com\/msftvisualcpp\">msftvisualcpp<\/a>).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Shareable link: https:\/\/aka.ms\/msvcsecurity \u70b9\u8fd9\u91cc\u770b\u4e2d\u6587\u7248 Every developer makes mistakes. No matter how careful you are when writing code, you will introduce bugs. And any bug can become a security vulnerability when software that runs in a connected environment or is used long past its initially planned lifespan. Code that isn&#8217;t correct is insecure code. The Microsoft [&hellip;]<\/p>\n","protected":false},"author":312,"featured_media":35994,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[314],"class_list":["post-16275","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus","tag-security"],"acf":[],"blog_post_summary":"<p>Shareable link: https:\/\/aka.ms\/msvcsecurity \u70b9\u8fd9\u91cc\u770b\u4e2d\u6587\u7248 Every developer makes mistakes. No matter how careful you are when writing code, you will introduce bugs. And any bug can become a security vulnerability when software that runs in a connected environment or is used long past its initially planned lifespan. Code that isn&#8217;t correct is insecure code. The Microsoft [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/16275","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/users\/312"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=16275"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/16275\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/35994"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=16275"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=16275"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=16275"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}